Steve Jessop 已经指出了为什么需要抽象类和虚函数。您可以强制使用 vtable 来实现动态调度。
您的抽象基类声明了派生类的共同特征。如果在编译时无法确定对象的类型,则虚函数确保调用正确的函数。
在以下示例中,class other
在编译时不知道class shape
提供了哪个派生。唯一知道的是:每个shape
派生都将为该center
方法提供一个实现,因为它是pure virtual
。(否则程序不会编译和链接。)这足以提供功能。该程序将shPtr
在运行时确定 的类型并调用正确的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;
}
输出是
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
完全正确(意味着已经调用了正确的中心函数)。
-为清楚起见进行编辑-
SuvP 的动物示例也几乎适用。它的缺点是不需要继承来实现相同的功能。
我稍微调整了一下,试图让纯虚拟的好处变得清晰。首先,用户可以任意添加Animal
,并且实现feed_all_animals
独立于当前派生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;
}