1. 为什么需要虚析构函数:当使用new运算符动态分配内存时,基类的析构函数就应该定义为虚析构函
数,不然就会出问题。比如类B由类A继承而来,则有语句A *p= new A;delete p; 这时没有问题 ,调
用类A的析构函数释放类A的资源。但如果再把类B的内存动态分配给指针p时如p= new B; delete p;如果
基类的析构函数不是虚析构函数的话就会只调用基类A中的析构函数释放资源, 而不会调用派生类B的析
构函数, 这时派生类B的资源没有被释放。
2. 解决这个问题的方法是把基类的析构函数声明为虚析构函数,即在析构函数前加virtual 关见字,定
义为虚析构函数时当用delete释放派生类的资源时就会根据基类的析构函数自动调用派生类中的析构函数
释放派生类的资源。
3. 只要基类中的析 构函数是虚析构函数 ,则该基类的派生类中的析 构函数自动为虚析构函数 ,虽然
派生类中的析构函数前没有virtual关见字,析构函数名字也不一样,但派生类中的析 构函数被自动继承
为虚析构函数 。
4. 如果要使用 运算符分配内存,最好将析构函数定义为虚析构函数。
使用 new 分配内存,但不定义为虚析构函数的情形:
class A{
public:
int a;
~A(){
cout<<"析构A"<<endl;
}
};
class B:public A
{
public:
int b;
~B(){
cout<<"析构B"<<endl;
}
};
class C:public B
{
public:
int c;
~C(){
cout<<"析构C"<<endl;
}
};
int main()
{
A *p = new A;
delete p; //输出析构A
//B m;
//p=&m;//此语句没有错,但是将使指针p指向一个静态分配的内存地址,这时不能用delete语句释放指针p的资源。
//delete p;
//错误,指针p现在指向的内容不是动态分配的内存,而是静态内存,delete只能释放动态分配的内存。
p=new B;//动态分配派生类B的内存,并把地址赋给指针p。
delete p; //输出析构A 在这里没有调用派生类的析构函数释放动态分配的派生类的内存资源。
B * p1 = new B;
delete p1;//输出析构A 析构B
p1 = new C;
delete p1;//输出析构A 析构B 注意,这里没有释放掉子类C的资源。
}
使用 new 分配内存,且基类定义为虚析构函数的情形:
class A{
public:
int a;
virtual ~A(){
cout<<"析构A"<<endl;
}//基类定义为虚析构函数
};
class B:public A
{
public:
int b;
~B(){
cout<<"析构B"<<endl;
}//派生类B自动继承为虚析构函数
};
class C:public B
{
public:
int c;
~C(){
cout<<"析构C"<<endl;
}//派生类C也自动继承为虚析构函数
};
int main()
{
A *p = new A;
delete p;//输出析构A
p=new B;
delete p;
//输出析构A 析构B 因为基类定义的是虚析构函数,所以在这里调用派生类的析构函数释放动态分配的派生类的内存资源,并调用基类的析构函数释放基类的资源
B * p1 = new B;
delete p1;//输出析构A 析构B
p1 = new C;
delete p1;//输出析构A 析构B 析构C 这里因为类B的析构函数被自动继承为虚析构函数,所以这里释放了子类C的释源。
}
数,不然就会出问题。比如类B由类A继承而来,则有语句A *p= new A;delete p; 这时没有问题 ,调
用类A的析构函数释放类A的资源。但如果再把类B的内存动态分配给指针p时如p= new B; delete p;如果
基类的析构函数不是虚析构函数的话就会只调用基类A中的析构函数释放资源, 而不会调用派生类B的析
构函数, 这时派生类B的资源没有被释放。
2. 解决这个问题的方法是把基类的析构函数声明为虚析构函数,即在析构函数前加virtual 关见字,定
义为虚析构函数时当用delete释放派生类的资源时就会根据基类的析构函数自动调用派生类中的析构函数
释放派生类的资源。
3. 只要基类中的析 构函数是虚析构函数 ,则该基类的派生类中的析 构函数自动为虚析构函数 ,虽然
派生类中的析构函数前没有virtual关见字,析构函数名字也不一样,但派生类中的析 构函数被自动继承
为虚析构函数 。
4. 如果要使用 运算符分配内存,最好将析构函数定义为虚析构函数。
使用 new 分配内存,但不定义为虚析构函数的情形:
class A{
public:
int a;
~A(){
cout<<"析构A"<<endl;
}
};
class B:public A
{
public:
int b;
~B(){
cout<<"析构B"<<endl;
}
};
class C:public B
{
public:
int c;
~C(){
cout<<"析构C"<<endl;
}
};
int main()
{
A *p = new A;
delete p; //输出析构A
//B m;
//p=&m;//此语句没有错,但是将使指针p指向一个静态分配的内存地址,这时不能用delete语句释放指针p的资源。
//delete p;
//错误,指针p现在指向的内容不是动态分配的内存,而是静态内存,delete只能释放动态分配的内存。
p=new B;//动态分配派生类B的内存,并把地址赋给指针p。
delete p; //输出析构A 在这里没有调用派生类的析构函数释放动态分配的派生类的内存资源。
B * p1 = new B;
delete p1;//输出析构A 析构B
p1 = new C;
delete p1;//输出析构A 析构B 注意,这里没有释放掉子类C的资源。
}
使用 new 分配内存,且基类定义为虚析构函数的情形:
class A{
public:
int a;
virtual ~A(){
cout<<"析构A"<<endl;
}//基类定义为虚析构函数
};
class B:public A
{
public:
int b;
~B(){
cout<<"析构B"<<endl;
}//派生类B自动继承为虚析构函数
};
class C:public B
{
public:
int c;
~C(){
cout<<"析构C"<<endl;
}//派生类C也自动继承为虚析构函数
};
int main()
{
A *p = new A;
delete p;//输出析构A
p=new B;
delete p;
//输出析构A 析构B 因为基类定义的是虚析构函数,所以在这里调用派生类的析构函数释放动态分配的派生类的内存资源,并调用基类的析构函数释放基类的资源
B * p1 = new B;
delete p1;//输出析构A 析构B
p1 = new C;
delete p1;//输出析构A 析构B 析构C 这里因为类B的析构函数被自动继承为虚析构函数,所以这里释放了子类C的释源。
}