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;
}