使用继承的时候有一些需要注意的地方,就是如果你真的要用new来创建这些有继承关系的类的实例,那每一个类的析构函数都要声明为virtual,否则会在一些奇怪的地方出现问题。这个细节在这一章后面的课程会提到。如果一个类(Cat)的父类(Animal)的析构函数是virtual的,那么他自己的析构函数就自动virtual

想象一下你获得了一个Animal* pAnimal;,你怎么知道他到底是Cat还是Dog呢?C++ 的dynamic_cast就可以帮到你。当然使用这个功能的前提是,Animal类(或者他的父类——如果有的话)必须至少要包含一个virtual函数。

int main()
{
    Animal* pAnimal = GetAnAnimalFromSomewhere();
    Cat* cat = dynamic_cast<Cat*>(pAnimal);
    if (cat == nullptr)
    {
        cout << "这不是一只猫。" << endl;
    }
    else
    {
        cout << "这是一只猫。" << endl;
    }
}

当然你也可以使用static_cast。它跟dynamic_cast的区别就是,他不回去检查pAnimal到底是不是Cat,就直接转换给你。如果不是的话,你就会拿到一个野指针,使用它就会发生 undefined behavior。

如果你一个类继承下来,并没有覆盖任何虚函数(析构函数不算),那你的设计很有可能出现了问题。这代表你的这个继承可以被一些其他的东西代替,譬如说红色的猫,你可以写一个函数专门构造一只猫然后把颜色改成红色,而不是继承下来,就只在构造函数里面改颜色。

dynamic_cast和 visitor 模式其实各有利弊。

dynamic_cast的好处:

  • A:代码写起来直接。
  • B:每次添加新的子类,这个类和关于它的一组不同的逻辑,可以通过抽取函数来组织到同一个文件里。

dynamic_cast的坏处:

  • C:当你需要因为多个类型的不同而做不同的事情的时候,多个ifdynamic_cast会导致性能低下(虽然其实不是很严重),代码结构也会相当混乱。
  • D:当你添加一个新的子类的时候,你要找到所有需要添加新分支的if,哪怕你测试足够,也很容易出错。
  • E:添加关于所有类的一个新逻辑的时候,你又要写一大堆if,不仅混乱,而且编译器还无法查漏补缺,很容易犯错误。

visitor 模式的好处:

  • C:因为这毕竟是一个O(1)跳转,不同的类只需要跳转一次就可以运行相应的函数(而不是用if一个一个尝试),性能高。
  • D:当你添加一个新的子类的时候,只要你 visitor 接口的成员都是纯虚类,你很容易通过阅读所有编译错误来找出所有需要修改。
  • E:添加关于所有的类的一个新的逻辑很容易,就是继承自接口,井井有条。

visitor 模式的坏处:

  • A:代码写起来比较曲折。
  • B:每次添加新的子类,都会修改很多相关的文件。但是在编译器的编译错误的帮助下,其实这并不是一个令人恐怖的问题,只是看起来恐怖。

当然,哪怕 visitor 模式不好用,大家也不要立刻就使用dynamic_cast来搞乱自己的代码,二十四个设计模式里面还有很多其他的内容可供选择。

visitor 模式的一个例子

#include <iostream>
#include <string>
#include <vector>
#include <memory>

using namespace std;

class Cat;
class Dog;
class Mouse;

class Animal
{
   
public:
    class IVisitor {
   
    public:
        virtual ~IVisitor() {
   
        }
        virtual void Visit(Cat *animal) = 0;
        virtual void Visit(Dog *animal) = 0;
        virtual void Visit(Mouse *animal) = 0;
    };
private:
    string name;

public:
    Animal(const string& theName)
        :name{
   theName}
    {
   
    }

    virtual ~Animal()
    {
   
    }

    const string& GetName()const
    {
   
        return name;
    }

    virtual string Introduce()const = 0;
    virtual void Accept(IVisitor *visitor) = 0;
};

class Cat : public Animal
{
   
public:
    Cat(const string& theName)
        :Animal{
   theName}
    {
   
    }

    string Introduce()const override
    {
   
        return "我是一只猫,我的名字叫\"" + GetName() + "\"。";
    }
    
    void Accept(IVisitor *visitor) override {
   
    	visitor->Visit(this);
    }
};

class Dog : public Animal
{
   
public:
    Dog(const string& theName)
        :Animal{
   theName}
    {
   
    }

    string Introduce()const override
    {
   
        return "我是一只狗,我的名字叫\"" + GetName() + "\"。";
    }
    void Accept(IVisitor *visitor) override {
   
    	visitor->Visit(this);
    }
};

class Mouse : public Animal
{
   
public:
    Mouse(const string& theName)
        :Animal{
   theName}
    {
   
    }

    string Introduce()const override
    {
   
        return "我是一只老鼠,我的名字叫\"" + GetName() + "\"。";
    }
    void Accept(IVisitor *visitor) override {
   
    	visitor->Visit(this);
    }
};

class OnlyPrintCatVisitor : public Animal::IVisitor {
   
public:
        void Visit(Cat *animal) override {
   
        	cout << animal->Introduce() << endl;
        }
        void Visit(Dog *animal) override {
   
            
        }
        void Visit(Mouse *animal) override {
   
            
        } 
};

int main()
{
   
    auto tom = make_shared<Cat>("Tom");
    auto jerry = make_shared<Mouse>("Jerry");
    auto spike = make_shared<Dog>("Spike");
    auto butch = make_shared<Cat>("Butch");
    auto lightning = make_shared<Cat>("Lightning");
    vector<shared_ptr<Animal>> friends{
    tom, jerry, spike, butch, lightning };
	OnlyPrintCatVisitor visitor;
    for (auto animal : friends)
    {
   
        animal->Accept(&visitor);
    }
    return 0;
}

以上来自计蒜客:)