C++ 的那些坑 (Day 2)
虚函数调用的例外
我们知道在通过基类的指针或者引用调用某个对象的函数时,如果这个对象是一个派生类而且该方法是一个虚方法那么一般情况下就会调用派生类的虚方法实现。这个过程是C++的多态。然而这之中有些例外,如果当我们在基类的构造函数或者析构函数中调用相关的虚方法时,他们被绑定到本类层级里的方法实现,而不会去动态的调用子类中的方法实现。如下面这个例子测试了析构函数中调用虚函数的情况
class Water {
public:
virtual void print(const string& msg) {
cout<<"Water:"<<msg<<endl;
}
virtual ~Water() {
print("ran out of water");
}
};
class Rain : public Water {
public:
virtual void print(const string& msg) {
cout<<"Rain:"<<msg<<endl;
}
virtual ~Rain() {
print("rain stop");
}
};
int main() {
{
shared_ptr<Rain> rain{new Rain};
}
return 0;
}
上述程序运行输出:
Rain:rain stop
Water:ran out of water
C++ Primer给出的解释大概意思是每个构造函数负责构造初始化本类成员,如果允许在构造函数内部使用虚函数那边子类的函数执行时器数据成员或者相关的状态都没初始化运行结果不能确定,同样析构时由于析构是从子类向基类逐渐进行,那在基类析构函数中调用虚函数会使得(子类中的)虚函数实现使用一些(子类)已经销毁的数据成员或者状态,造成不确定的后果。所以C++中就这么规定了,构造函数和析构函数中的虚函数调用其实在编译其就已经绑定了。
虚函数默认参数
虚函数绑定虽然是在运行时进行的,但是默认参数则在编译期已经决定。
class Base {
public:
virtual void print(int value = 1) {
cout<<value<<endl;
}
};
class Derived : public Base {
public:
virtual void print(int value = 2) {
cout<<value<<endl;
}
};
int main() {
{
shared_ptr<Base> b{new Derived};
b->print();
}
return 0;
}
程序运行输出:
1