首先看下面的例子
输出结果为
首先程序找到了A::GetName(). 然而这个函数是虚函数,所以接下来程序找下一层继承B::GetName(), 然后再找下一层C::GetName()。程序不会去找D中的函数,因为我们的类对象来自于C。最后程序的结果为
请注意,我们没有声明 Animal::GetName()为virtual。这时因为GetName()函数用不着在派生类中重置(override)。所以只有函数需要在派生类中重置的时候才需要声明虚函数,另外重置和重载(overload)是不同的概念。
结果应该是
class Base
{
protected:
public:
const char* GetName() { return "Base"; }
};
class Derived: public Base
{
public:
const char* GetName() { return "Derived"; }
};
int main()
{
Derived cDerived;
Base &rBase = cDerived;
cout << "rBase is a " << rBase.GetName() << endl;
}
输出结果为
rBase is a Base
因为rBase是基类指针,它将调用Base::GetName(),而实际上它指向的是派生类的基类部分。
Virtual functions
A virtual function is a special type of function that resolves to the most-derived version of the function with the same signature.
请注意virtual function 和 virtual base classes 是两个完全不同的概念。
使用虚函数重写上面的例子
class Base
{
protected:
public:
virtual const char* GetName() { return "Base"; }
};
class Derived: public Base
{
public:
virtual const char* GetName() { return "Derived"; }
};
int main()
{
Derived cDerived;
Base &rBase = cDerived;
cout << "rBase is a " << rBase.GetName() << endl;
return 0;
}
这时输出结果为
rBase is a Derived
因为rBase是指向派生类对象的基类部分的指针,所以当调用rBase.GetName()时,程序会调用Base::GetName(). 但是,Base::GetName()是虚函数,这就使得程序派生类中有没有同名的函数。因为rBase指向的是派生类Derived的一部分,所以程序就寻找位于Base和Derived之间的所有继承类。然后选在最靠近派生类的那一层的同名函数。
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 cClass;
A &rBase = cClass;
cout << "rBase is a " << rBase.GetName() << endl;
return 0;
}
首先程序找到了A::GetName(). 然而这个函数是虚函数,所以接下来程序找下一层继承B::GetName(), 然后再找下一层C::GetName()。程序不会去找D中的函数,因为我们的类对象来自于C。最后程序的结果为
rBase is a C
A more complex example
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 "Meow"; }
};
class Dog: public Animal
{
public:
Dog(std::string strName)
: Animal(strName)
{
}
const char* Speak() { return "Woof"; }
};
下面是有虚函数的版本
class Animal
{
protected:
std::string m_strName;
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 "Meow"; }
};
class Dog: public Animal
{
public:
Dog(std::string strName)
: Animal(strName)
{
}
virtual const char* Speak() { return "Woof"; }
};
请注意,我们没有声明 Animal::GetName()为virtual。这时因为GetName()函数用不着在派生类中重置(override)。所以只有函数需要在派生类中重置的时候才需要声明虚函数,另外重置和重载(overload)是不同的概念。
通过使用virtual speak函数,下面的代码应该可以正常工作
void Report(Animal &rAnimal)
{
cout << rAnimal.GetName() << " says " << rAnimal.Speak() << endl;
}
int main()
{
Cat cCat("Fred");
Dog cDog("Garbo");
Report(cCat);
Report(cDog);
}
结果应该是
Use of the virtual keyword
从技术上讲,派生类并不需要virtual这个关键字。只有最最开始的那个基类需要使用声明virtual。但是在派生类中保留virtual是个很好的编程习惯。
Return types of virtual functions
一般情况下来说,虚函数的返回类型应该一样。下面的代码就不对
class Base
{
public:
virtual int GetValue() { return 5; }
};
class Derived: public Base
{
public:
virtual double GetValue() { return 6.78; }
};
然后,有一种特例,当虚函数的返回类型是类对象的指针或者引用时,重置的函数可以返回派生类的指针或引用
class Base
{
public:
// This version of GetThis() returns a pointer to a Base class
virtual Base* GetThis() { return this; }
};
class Derived: public Base
{
// Normally override functions have to return objects of the same type as the base function
// However, because Derived is derived from Base, it's okay to return Derived* instead of Base*
virtual Derived* GetThis() { return this; }
};