构造函数为什么不能为虚函数?析构函数为什么要虚函数?

scorlw 发布于

构造函数为什么不能为虚函数?析构函数为什么要虚函数?

c++

1.构造函数为什么不能是虚函数?

a.最直观的的一个角度是,虚函数使得对象内部存在一个指向虚函数表的指针,通过该指针指向的虚函数表确定调用的函数。而在调用构造函数时,对象还没有生成,就根本谈不上虚函数表和虚函数指针了。

b.虚函数的调用往往是基于“动态联编”的,即在对象生成之后才能确定调用的是基类中的函数还是派生类中的重写函数。在调用构造函数时,对象还未构造成功,编译器无法知道对象的实际类型,是对象本身,还是派生类对象或者派生类的派生类,自然也无法实现“动态”的调用。

2.析构函数为什么(某些情况下)必须是虚函数?

首先明确的是,“某些情况”指的是该类作为基类时,其析构函数必须是虚函数。这是因为,指向基类的指针可以指向派生类对象,如果不声明析构函数为虚函数的话,在一个由基类指针指向的派生类对象析构时,那么只会调用父类的析构函数,自身的析构函数不会被调用,甚至会造成内存泄漏。只有当声明为virtual类型时,才能够先执行派生类自身的析构函数,再执行基类的析构函数,确保执行的顺序正确。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//基类
class A{
public :
A(){ cout<<"A构造函数"<<endl; }
~A(){cout<<"A被销毁了"<<endl;}
void Do(){
cout<<"A要做点什么"<<endl;
}
};
//派生类
class B :public A{
public :
B(){ cout<<"B构造函数"<<endl;}
~B(){ cout<<"B被销毁了"<<endl;}
void Do(){ cout<<"B要做点什么"<<endl;}
};

(1)派生类 指针=new 派生类;

1
2
3
4
5
6
7
8
int main()
{
B *p = new B;//那么就会执行基类构造函数,派生类构造函数
p->Do();//通过派生类指针可以调用派生类的成员函数
delete p;//先调用派生类析构函数,在调用基类析构函数
system("pause");
return 0;
}

输出:

1
2
3
4
5
A构造函数
B构造函数
B要做点什么
B被销毁了
A被销毁了

(2)派生类 指针=new 基类; 会出错,基类指针不能转换成派生类指针.

(3)基类 指针 = new 派生类; (派生类指针转化成基类指针)

1
2
3
4
5
6
7
8
int main()
{
A *p = new B;//依旧要先调用基类构造函数,再派生类构造函数
p->Do();//通过基类指针调用基类成员函数,此处只能调用基类里面有的成员函数,当调用派生类中成员函数会提示基类中并没有这个成员
delete p;//这里只会调用基类的析构函数,所以内存释放并不完全
system("pause");
return 0;
}

输出:

1
2
3
4
A构造函数
B构造函数
A要做点什么
A被销毁了

(4)上面(3)知道了这样影响了内存的释放完整程度,所以我们通过引入虚函数机制,将基类的析构函数定义成虚函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//基类

class A
{
public:
A() { cout << "A构造函数" << endl; }
virtual ~A() { cout << "A被销毁了" << endl; }
void Do()
{
cout << "A要做点什么" << endl;
}
};
//派生类

class B : public A
{
public:
B() { cout << "B构造函数" << endl; }
~B() { cout << "B被销毁了" << endl; }
void Do() { cout << "B要做点什么" << endl; }
};

main()函数如(3),输出:

1
2
3
4
5
A构造函数
B构造函数
A要做点什么
B被销毁了
A被销毁了

原文地址:https://blog.csdn.net/zkangaroo/article/details/57000397