为啥继承时基类的析构一般声明为虚函数

来源:互联网 发布:sql删除表中全部数据 编辑:程序博客网 时间:2024/05/21 22:50

1、为啥继承时基类的析构函数声明为虚函数?

文字描述太抽象了,直接用代码给出答案。

(1)第一段代码:

#include<iostream>using  namespace std ;class Base{public:Base(){cout << "基类的构造函数被调用了" << endl;} ~Base(){cout << "基类的析构函数被执行了" <<endl;}};class Derived :public Base{public:Derived(char *str = ""){if (str == NULL){_Dstr = new char[1];*_Dstr = '\0';}else{_Dstr = new char [strlen(str) + 1];strcpy(_Dstr, str);}cout << "派生类的构造函数被调用了" << endl;}~Derived(){if (_Dstr != NULL){delete [] _Dstr;_Dstr = NULL;}cout << "派生类的析构函数被执行了" <<endl;}private:char *_Dstr;};void test(){Base B1;Base & b = B1;   //基类的引用或指针指向基类对象。}int main(){test();cout << "hello..." <<endl;system("pause");return 0;}


(2)第二段代码

#include<iostream>using  namespace std ;class Base{public:Base(){cout << "基类的构造函数被调用了" << endl;} ~Base(){cout << "基类的析构函数被执行了" <<endl;}};class Derived :public Base{public:Derived(char *str = ""){if (str == NULL){_Dstr = new char[1];*_Dstr = '\0';}else{_Dstr = new char [strlen(str) + 1];strcpy(_Dstr, str);}cout << "派生类的构造函数被调用了" << endl;}~Derived(){if (_Dstr != NULL){delete [] _Dstr;_Dstr = NULL;}cout << "派生类的析构函数被执行了" <<endl;}private:char *_Dstr;};void test(){Base * b = new Derived("str");   //基类的引用或指针指向派生类对象。delete b;}int main(){test();cout << "hello..." <<endl;system("pause");return 0;}


//分析答案发现析构时,只调用了基类的析构函数,没有调用派生类的析构函数,导致派生类内存泄露,


(3)第三段代码

#include<iostream>using  namespace std ;class Base{public:Base(){cout << "基类的构造函数被调用了" << endl;} virtual ~Base()  //将基类的析构函数声明为虚函数。 {cout << "基类的析构函数被执行了" <<endl;}};class Derived :public Base{public:Derived(char *str = ""){if (str == NULL){_Dstr = new char[1];*_Dstr = '\0';}else{_Dstr = new char [strlen(str) + 1];strcpy(_Dstr, str);}cout << "派生类的构造函数被调用了" << endl;}~Derived(){if (_Dstr != NULL){delete [] _Dstr;_Dstr = NULL;}cout << "派生类的析构函数被执行了" <<endl;}private:char *_Dstr;};void test(){Base * b = new Derived("str");   //基类的引用或指针指向派生类对象。delete b;}int main(){test();cout << "hello..." <<endl;system("pause");return 0;}


//答案分析:析构时,先调用了派生类的析构函数,再调用基类的析构函数,不存在内存泄露。反过来看源代码只是发现只是将基类的析构函数声明为虚函数。


结论: 继承时,如果派生类有资源需要自己手动释放,最好将基类的析构函数声明为虚函数,这样我们可以通过基类的指针或引用去释放子类的资源。防止内存泄露。


2.当我们想要继承一个抽象类的时候,但是还不能确定抽象类的那些成员是纯虚函数,我们可以将抽象类的析构函数声明为纯虚函数。

(1)第一段代码

#include<iostream>using  namespace std ;class Abstract{public:virtual ~Abstract() = 0;};class Derived: public Abstract{public:Derived(){cout << "派生类的构造函数被执行了" <<endl;}~Derived(){cout << "派生类的析构函数被执行了" << endl;}};void test(){Derived d;}int main(){test();cout << "hello..." <<endl;system("pause");return 0;}

如果我们单纯的直接这么写,我们发现编译器报错,报下列的错误:


抽象类的析构函数.obj : error LNK2019: 无法解析的外部符号 "public: virtual __thiscall Abstract::~Abstract(void)" (??1Abstract@@UAE@XZ),该符号在函数 __unwindfunclet$??0Derived@@QAE@XZ


通过分析:我们发现定义派生类对象时,会先调用抽象类的构造函数,抽象类的构造函数没有显示的给出,编译器合成一个默认的构造函数,再调用派生类的构造函数,析构时,先调用派生类的析构函数,最后调用抽象类的析构函数,但是抽象类的析构函数已经显示的声明了,因此会调用程序员自己写的,但是这个析构函数没有函数体,所以编译器报错,说无法解析的外部符号,

解决方案:给抽象类的析构函数定义。看下面的代码:

(2)第二段代码:

#include<iostream>using  namespace std ;class Abstract{public:virtual ~Abstract() = 0;}; Abstract:: ~Abstract(){cout << "抽象类的析构函数被执行了" <<endl;};class Derived: public Abstract{public:Derived(){cout << "派生类的构造函数被执行了" <<endl;}~Derived(){cout << "派生类的析构函数被执行了" << endl;}};void test(){Derived d;}int main(){test();cout << "hello..." <<endl;system("pause");return 0;}






0 0
原创粉丝点击