Reputation: 11968
Let's take an example,
class base{
public:
virtual void abstract() = 0;
};
class derived:public base{
public:
void abstract(){cout << "Abstract\n";}
};
int main{
derived d;
d.abstract();
return 0;
}
It can be written in other way as,
class base{
public:
void abstract(){cout << "Abstract\n";}
};
int main{
base b;
b.abstract();
return 0;
}
It is also providing same result, infact here I don't need to derive the class.
I did read many article on abstract class, it says we can not instantiate base class
and a pure virtual function
forces user to define the function
.
But if we will see the above code in both the cases I am getting same result (or output
).
So here my question is how abstract
classes helps us?
Upvotes: 5
Views: 231
Reputation: 24956
Steve Jessop already pointed out why abstract classes and virtual functions are needed. You can force a vtable to be used to achieve dynamic dispatch.
Your abstract base class declares the common features for your derived classes. The virtual function ensures that the correct function is called if the object's type cannot be determined at compile time.
In the following example, the class other
does not know at compile time which derivate of class shape
is provided.
The only thing known is: each shape
derivate will provide an implementation for the center
method since it is pure virtual
. (The program won't compile and link otherwise.)
This is sufficient to provide functionality.
The program will determine the type of shPtr
at runtime and call the correct center()
.
#include <string>
#include <iostream>
using namespace std;
class v2d
{
public:
double x, y;
v2d (void) : x(0.0), y(0.0) { }
v2d (double const a, double const b) : x(a), y(b) { }
};
class shape
{
public:
string name;
shape (void) : name() { }
shape (string const n) : name(n) { }
virtual v2d center (void) const = 0;
};
class circle : public shape
{
private:
v2d center_point;
double radius;
public:
circle (void) : shape("Circle"), center_point(), radius(0.0) { }
circle (v2d const cp, double const r) : shape("Circle"), center_point(cp), radius(r) { }
v2d center (void) const { return center_point; }
};
class square : public shape
{
private:
v2d lowright;
double sidelength;
public:
square (void) : shape("Square"), lowright(), sidelength(0.0) { }
square (v2d const tl, double const sl) : shape("Square"), lowright(tl), sidelength(sl) { }
v2d center (void) const
{
double const halflen = sidelength/2.0;
return v2d(lowright.x+halflen, lowright.y+halflen);
}
};
class other
{
private:
shape *shPtr;
public:
other (void) : shPtr(NULL) { }
other (shape *sh_ptr) : shPtr(sh_ptr) { }
void doSomething (void)
{
cout << "Center of this Shape, which is a " << shPtr->name << " is: "<< shPtr->center().x << ", " << shPtr->center().y << endl;
}
};
int main (void)
{
v2d sq_c(1.0, 2.0), circ_c(4.0, 4.0);
square square_obj(sq_c, 5.0);
circle circle_obj(circ_c, 2.0);
other other1 (&square_obj), other2(&circle_obj);
cout << fixed << setprecision(2);
other1.doSomething();
other2.doSomething();
return 0;
}
The output is
Center of this Shape, which is a Square is: 3.50, 4.50
Center of this Shape, which is a Circle is: 4.00, 4.00
being perfectly correct (meaning the correct center functions have been called).
-Edit for clarity-
The animals example of SuvP is almost suited, too. It has the downside that no inheritance would be needed to achieve the same functionality.
I adjusted it a litte, trying to make the benefit of pure virtuality clear.
First of all the user can add any arbitrary Animal
and the implementation of feed_all_animals
is independant of the present derived Animal
s.
#include <string>
#include <iostream>
#include <vector>
using namespace std;
class Animal
{
public:
virtual string food() = 0;
virtual string name() = 0;
void eat() { cout << "A " << name() << " is eating " << food() << endl; }
};
class Dog:public Animal
{
public:
string name() { return "Dog"; }
string food() { return "Meat"; }
};
class Horse:public Animal
{
public:
string name() { return "Horse"; }
string food() { return "Gras"; }
};
void feed_all_animals (vector<Animal*> animals)
{
for (size_t i=0; i<animals.size(); ++i)
{
cout << "Feeding animal " << i+1 << " (a " << animals[i]->name() << ") with " << animals[i]->food() << endl;
animals[i]->eat();
}
}
int main (void)
{
vector<Animal*> animals;
// We have a zoo with three dogs and two horses
Dog dog1, dog2, dog3;
Horse horse1, horse2;
animals.push_back((Animal*)&dog1);
animals.push_back((Animal*)&dog2);
animals.push_back((Animal*)&dog3);
animals.push_back((Animal*)&horse1);
animals.push_back((Animal*)&horse2);
// now we let the user add another animal
int ani(0);
cout << "Do you want to add a Horse [1] or a Dog [0]: ";
cin >> ani;
switch (ani)
{
case 1: animals.push_back((Animal*)new Horse); break;
default: animals.push_back((Animal*)new Dog); break;
}
// so they don't starve
feed_all_animals(animals);
delete animals[animals.size()-1];
animals.clear();
return 0;
}
Upvotes: 1
Reputation: 279355
As you've noticed, in your example code there is no point having separate base and derived classes.
In general, the purpose of classes with virtual functions is dynamic polymorphism, but your code hasn't used it.
Suppose you had some code that used a base*
without knowing which (of several) derived classes it actually points to. Suppose that they each have different implementations of abstract()
. Suppose that you want to force anyone who writes a derived class of base
to implement their own version of abstract()
. Then there's a reason to make abstract()
a pure virtual function and hence a reason for base
to be an abstract class:
#include <iostream>
#include <cstdlib>
#include <ctime>
struct Base {
virtual void abstract() = 0;
virtual ~Base() {};
};
struct Derived1 : Base {
void abstract() { std::cout << "Derived1\n"; }
};
struct Derived2 : Base {
void abstract() { std::cout << "Derived2\n"; }
};
Base *random_object() {
if (std::rand() < RAND_MAX/2) {
return new Derived1();
} else {
return new Derived2();
}
}
int main() {
std::srand(std::time(0));
Base *b = random_object();
b->abstract();
delete b;
}
The code in main
only cares about Base
, it doesn't need to know anything about derived classes beyond the fact that there might be some. Only the code in random_object
knows about the derived classes. Meanwhile Base
doesn't need to know how abstract()
is implemented, only the derived classes do (and each one only cares about its own implementation). It's good for code not to need to know about things -- it means those things can be changed without touching the code that doesn't care.
There are also some high-level design issues around using a non-abstract class as a public base class. For example, it's quite easy to unthinkingly write code that won't work as you expect once some of your functions have been overridden, and it's quite easy to accidentally slice an object with a concrete base class. So there's a philosophy that says you should know at all times whether you're writing a concrete class or a base class, and not try to do both at once. When you follow this philosophy, all base classes are abstract, so you make a class abstract to signal that it is designed to be derived from.
Upvotes: 6
Reputation: 567
Here's an example for you: Let's say your program needs to calculate the area of different shapes.
class shape{
public:
virtual double area() = 0;
};
class triangle:public shape{
public:
triangle(double base, double height) : b(base), h(height) {}
double area(){ return 0.5 * b * h; }
private:
double b;
double h;
};
class circle:public shape{
public:
circle(double radius) : r(radius) {}
double area(){ return M_PI * r * r; }
private:
double r;
};
int main{
std::vector<shape*> shapes;
//add whatever shapes you want here
shapes.push_back( new triangle(4, 5) );
shapes.push_back( new circle(3) );
double dTotal = 0.0;
std::vector<shape*>::iterator i;
for (i = shapes.begin(); i != shapes.end(); i++)
{
dTotal += (*i)->area();
delete *i;
}
cout << dTotal;
return 0;
}
You could now very easily create shapes for rectangle, circle, dodecahedron, etc and treat them all similarly without knowing the specifics of how they compute their own areas. At the same time, it doesn't make sense for shape
to define an area of its own.
EDIT: added another derived class and made use of them using the abstract method.
Upvotes: 2
Reputation: 5239
Abstract Classes can also be helpful in cases where you don't want an object to be created of a Class.
Consider this,
/* Abstract Class Animal */
class Animal
{
public:
virtual void eat() = 0;
virtual void walk() = 0;
};
class Dog:public Animal
{
public:
void walk() { std::cout<<"\nWalk with Paws"; }
void eat() { std::cout<<"\nEat meat"; }
};
class Horse:public Animal
{
public:
void walk() { std::cout<<"\nWalk with Hooves"; }
void eat() { std::cout<<"\nEat Grass"; }
};
int main()
{
Dog d;
Horse h;
d.eat();
d.walk();
h.eat();
h.walk();
return 0;
}
Here , you don't want an object of type Animal
to be created. That object doesn't have any specific behaviour for sleep
eat
etc.
You can utilise this abstract
class for creating subclasses like Dog
, Cat
etc
pure virtual functions
have to be implemented by the derived class or else the compiler gives an error.
This guarantees all your sub classes will have the desired methods and also you don't end up creating a undefined
object such as Animal
Upvotes: 1
Reputation: 45715
In "abstract" words, abstract classes (also called interfaces when fully abstract) abstract from the implementation of a set of similar classes. Here, similar means they have the same set of functions but might implement them differently.
The abstract class itself doesn't define the implementation. Passing (pointers or references to) abstract classes around in your program only passes one "closed package" around, with which you can interact (e.g. shake). But you hide what's inside the package, including how the interface is implemented (e.g. what happens when you shake it).
That said, abstract classes just take the abstraction one level higher. A class describes what attributes and behavior an object has, but an abstract class only describes what you can do with an object. Only an actual implementation defines the behavior.
This being said, one important fact is that you can't create instances of an abstract class.
When designing a piece of software, abstract classes / interfaces are a very important method of abstraction. Whenever some piece of your software wants to interact with something it doesn't know in particular, you could write an interface which describes what features such an object has to provide without specifying the actual type. This makes it possible to easily pass objects of different types around. The function which takes a pointer / reference to such an interface / abstract class can interact with the object without knowing its actual type.
Upvotes: 1
Reputation: 13207
The purpose of an abstract (base-)class
is to define an interface
for sub-classes. This way you can determine what your class must be capable of (its methods), but not how it does things (the implementation).
When you derive from an abstract
class you promise to provide a certain behavior in your sub-class. Using this, functions can use your objects and perform their actions without knowing their specific behavior to the detail.
There are class-hierarchies all around, like in .NET
-framework to represent concepts or things like UI-controls.
Secondly, it sometimes does not make sense to have an instance of a class. Consider a class Animal
. How is Animal
defined? It doesn't make sense to instantiate an Animal
, but rather a Dog
or a Cat
, derived from Animal.
Upvotes: 1
Reputation: 129504
Abstract classes are used to provide an interface for some functionality, where the exact behaviour is defined by the caller's class. To ensure that the "user" of the function implements their functions, you use abstract functions in the base-class. That way, if the "user" doesn't provide one of the required functions, the code doesn't compile.
Upvotes: 3