12.2 - Virtual functions

首先看下面的例子

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

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);
}

结果应该是

Fred says Meow
Garbo says Woof

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; }
};




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值