使用继承的时候有一些需要注意的地方,就是如果你真的要用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:当你需要因为多个类型的不同而做不同的事情的时候,多个
if
和dynamic_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;
}
以上来自计蒜客:)