此篇文章主要就是围绕标题来阐述的,没有过多的其他的知识点的总结。
总结:
1.尽量不要在构造函数和析构函数中调用虚函数(注意这只是个建议,并不是说这样调用编译器一定会报错),因为这样的调用得不到你想要的结果。
为什么呢?下面看我来一步步分析
首先我们看下面这个代码
class Base
{
public:
Base()
{
cout << "Base::构造函数" << endl;
fun1();
}
~Base()
{
cout << "Base::析构函数" << endl;
}
virtual void fun1()
{
cout << "Base::fun1" << endl;
}
};
class Dervice :public Base
{
public:
Dervice()
{
cout << "Dervice::构造函数" << endl;
}
~Dervice()
{
cout << "Dervice::析构函数" << endl;
}
virtual void fun1()
{
cout << "Dervice::fun1" << endl;
}
};
int main()
{
Dervice s;
system("pause");
return 0;
}
解释如下:
因为在继承对象的构造体系中,在派生类的构造函数体进入前先调用基类的构造函数来初始化派生类中继承来自基类的这一部分,当在执行基类的构造函数的时候,编译器并不知道派生类对想中的任何信息,构造函数只会以为自己在初始化一个基类对象,所以这个时候调用fun1就会调用基类的fun1,不会发生动态联编产生多态的 效果。这样做是极不可取的一种方法。如果基类中有一个纯虚函数,你在构造函数中调用纯虚函数,必然会导致一个链接错误的信息发出,因为编译器在基类中没有找到纯虚函数的实现代码。
其实还有一种说法我觉得更加的可以解释这个问题,就是在初始化派生类对象的时候首先执行基类对象的构造函数,这个时候派生类的vptr指针还没有初始化好,此时的vptr指针正指向父类的虚表,所以只能在父类的虚表中找到fun1然后执行。
上述描述不是说在构造派生类中基类部分的时候,编译器不知道派生类信息吗,那如果在构造基类的时候需要子类的信息的话怎么办?
我们可以通过派生类将基类需要的信息传送给基类的的构造函数,这样就可以了,看如下代码:
class A
{
private:
int _x;
public:
A(int x) :_x(x){};
};
class B :public A
{
private:
int _y;
public:
B(int y) :A(y), _y(y){}
};
int main()
{
B s(10);
system("pause");
return 0;
}
在上述代码中初始化派生类中基类的_x需要用到派生类的参数的信息,我们就通过把信息传递给基类的构造函数。
2.为什么不能在析构函数中调用虚函数呢?
道理是一样的都不会发生动态联编的行为反而会增加代码的危险性。
下面我们加入可以在构造函数中按照我们的想法发生动态联编的调用虚函数的话会发生什么不可预估的错误
1.构造函数中调用虚函数,假如发生动态联编调用派生类的函数,这个时候派生类的构造函数还没有执行,对于派生类中的成员数据都是未定义的行为,所以这个时候就会产生错误。
2.析构函数中调用虚函数,当派生类析构函数先执行以后,派生类的数据成员就会编程未定义的,这个时候在基类的析构函数中如果调用虚函数如我们所愿为派生类的话,同样会产生错误。
所以:尽量不要在构造和析构函数中调用虚函数,因为这样做毫无意义,而且还会使代码容易产生错误。