作者:jicanmeng
时间:2014年12月05日
先看一个程序:
#include <iostream> #include <string> class Animal { protected: std::string m_strName; // We're making this constructor protected because // we don't want people creating Animal objects directly, // but we still want derived classes to be able to use it. Animal(std::string strName) : m_strName(strName) { } public: std::string GetName() { return m_strName; } const char* Speak() { return "???"; } }; class Cat: public Animal { public: Cat(std::string strName) : Animal(strName) { } const char* Speak() { return "Miao"; } }; int main() { using namespace std; Cat cCat("Fred"); cout << "cCat is named " << cCat.GetName() << ", and it says " << cCat.Speak() << endl; Animal *pAnimal = &cCat; cout << "pAnimal is named " << pAnimal->GetName() << ", and it says " << pAnimal->Speak() << endl; Animal &rAnimal = cCat; cout << "rAnimal is named " << rAnimal.GetName() << ", and it says " << rAnimal.Speak() << endl; return 0; }
以下是运行结果:
[jicanmeng@andy tmp]$ ./a.out
cCat is named Fred, and it says Miao
pAnimal is named Fred, and it says ???
rAnimal is named Fred, and it says ???
[jicanmeng@andy tmp]$
对结果的分析,learncpp.com上面解释的非常清楚(learncpp.com上面举了一个例子,父类名称为Base,子类名称为Derived,所以下面的说明中,注意Base对应于Animal,pBase对应于pAnimal,rBase对应于rAnimal):
It turns out that because rBase and pBase are a Base reference and pointer, they can only see members of Base (or any classes that Base inherited). So even though Derived::GetName() is an override of Base::GetName(), the Base pointer/reference can not see Derived::GetName(). Consequently, they call Base::GetName(), which is why rBase and pBase report that they are a Base rather than a Derived.
Note that this also means it is not possible to call Derived::GetValue() using rBase or pBase. They are unable to see anything in Derived.
假设现在除了定义了一个Cat子类,还定义了cDog,Wolf,Horse,Cow等等子类。我们需要实现一个函数,打印每个动物的名字和声音。对于Cat,函数如下:
void Report(Cat &cCat) { cout << cCat.GetName() << " says " << cCat.Speak() << endl; }
对于cDog,函数如下:
void Report(Dog &cDog) { cout << cDog.GetName() << " says " << cDog.Speak() << endl; }
如果对于每一个子类,我们都实现一个类似的函数,那么工作量就很大。
为了解决这样的问题,C++中引入了虚函数的概念。我们将上面的程序修改为虚函数的形式。
#include <iostream> #include <string> class Animal { protected: std::string m_strName; // We're making this constructor protected because // we don't want people creating Animal objects directly, // but we still want derived classes to be able to use it. Animal(std::string strName) : m_strName(strName) { } public: std::string GetName() { return m_strName; } virtual const char* Speak() { return "???"; } }; class Cat: public Animal { public: Cat(std::string strName) : Animal(strName) { } virtual const char* Speak() { return "Miao"; } }; int main() { using namespace std; Cat cCat("Fred"); cout << "cCat is named " << cCat.GetName() << ", and it says " << cCat.Speak() << endl; Animal *pAnimal = &cCat; cout << "pAnimal is named " << pAnimal->GetName() << ", and it says " << pAnimal->Speak() << endl; Animal &rAnimal = cCat; cout << "rAnimal is named " << rAnimal.GetName() << ", and it says " << rAnimal.Speak() << endl; return 0; }
以下是运行结果:
[jicanmeng@andy tmp]$ ./a.out
cCat is named Fred, and it says Miao
pAnimal is named Fred, and it says Miao
rAnimal is named Fred, and it says Miao
[jicanmeng@andy tmp]$
可以看出来,定义一个指针*pAnimal
指向一个派生类对象,但是指针本身是指向基类类型的,基类和派生类中都有虚函数Speak()
,那么pAnimal->Speak()调用的不是基类的Speak()函数,而是派生类的Speak()函数。
使用虚函数,前面提到的问题可以得到解决。我们将Speak()定义为虚函数,然后定义一个函数:
void Report(Animal &cAnimal) { cout << cAnimal.GetName() << " says " << cAnimal.Speak() << endl; }
这样,传进来的是Cat类型的对象,就调用Cat类的Speak()函数。传进来的是Dog类型的对象,就调用Dog类的Speak()函数。这种通过基类指针或引用,调用函数时根据指针指向的对象的类,来决定调用哪个函数的行为,就称为多态。多态是通过虚函数实现的。
learncpp.com上面是这样描述的:
"A virtual function is a special type of function that resolves to the most-derived version of the function with the same signature. To make a function virtual, simply place the “virtual” keyword before the function declaration."
对于virtual function,有四个需要注意的地方:
#include <iostream> class A { public: virtual const char* getName() { return "A"; } }; class B: public A { public: virtual const char* getName() { return "B"; } }; class C: public B { public: virtual const char* getName() { return "C"; } }; class D: public C { public: virtual const char* getName() { return "D"; } }; int main() { C c; A &rBase = c; std::cout << "rBase is a " << rBase.getName() << '\n'; return 0; }输出结果为"rBase is a C"。为什么是C而不是D呢?因为我们创建的就是一个C object,这是最终派生的对象。
#include <iostream> class A { public: virtual const char* getName() { return "A"; } }; class B: public A { public: const char* getName() { return "B"; } }; class C: public B { public: const char* getName() { return "C"; } }; class D: public C { public: const char* getName() { return "D"; } }; int main() { C c; A &rBase = c; std::cout << "rBase is a " << rBase.getName() << '\n'; return 0; }但是为了清楚明了,建议写上。
#include <iostream> class A { public: virtual const char* getName() const { return "A"; } }; class B: public A { public: const char* getName() { return "B"; } }; class C: public B { public: const char* getName() { return "C"; } }; class D: public C { public: const char* getName() { return "D"; } }; int main() { C c; A &rBase = c; std::cout << "rBase is a " << rBase.getName() << '\n'; return 0; }现在的输出结果就为"rBase is a A"。因为在base class中有一个const,而derived class中没有。
Remember that when a Derived class is created, the Base portion is constructed first. If you were to call a virtual function from the Base constructor, and Derived portion of the class hadn’t even been created yet, it would be unable to call the Derived version of the function because there’s no Derived object for the Derived function to work on. In C++, it will call the Base version instead.
A similar issue exists for destructors. If you call a virtual function in a Base class destructor, it will always resolve to the Base class version of the function, because the Derived portion of the class will already have been destroyed.