虚析构与纯虚析构
现在有两个类A,B。A是B的父类,此时A,B的构造、析构、拷贝函数都是public,B中有一个指针变量,B在构造函数中为这个变量申请内存空间(new),在析构函数中释放这个内存(delete)当在执行。此时如果用父类A定义一个B的指针对象并申请内存空间:A* a=new B();之后释放a所占内存:delete a;
在这种情况下,C++执行顺序是A_ctor,B_ctor,A_dtor.
为什么会出现这种情况呢?因为在子类中申请(new)的内存是在堆区中的。无法通过继承过来的父类来释放,父类只能释放自己类内部的指针。
这里给出的例子是水果与苹果的父子类
#include<iostream>
#include<string>
using namespace std;
//只要有类中有一个函数是纯虚函数,那么这个类就是一个抽象类,他是不能有实例对象的
// 如果类中除了构造、拷贝、析构函数,有一个函数是虚函数,那么这个类的子类必须重写这个虚函数,
// 带不带virtual都不影响代码正确性,但是为了程序的可读性,建议在子类重写虚函数的时候带上virtual
class Fruit1
{
public:
Fruit1()
{
cout << "fruit1 ctor" << endl;
kind = "水果";
}
~Fruit1()
{
cout << "fruit1 dtor" << endl;
}
virtual void printkind()//父类虚函数
{
cout << kind << endl;
}
private:
string kind;
};
class Apple :public Fruit1
{
public:
Apple()
{
//对堆中变量申请内存
a_name = new char();
a_kind = "苹果";
cout << "Apple ctor" << endl;
}
virtual ~Apple()
{
//释放内存
cout << "Apple dtor" << endl;
delete a_name;;
}
virtual void printkind()//父类中的虚函数必须在子类中重写!
{
cout << a_kind<<" "<<a_name << endl;
}
private:
char* a_name;
string a_kind;
};
void test01()
{
Fruit1* apple = new Apple();
delete apple;
}
int main()
{
test01();
return 0;
}
运行结果如下:
明显可以看到没有调用Apple的析构函数,那么Apple中的指针变量a_name申请的内存空间就不会释放,那么就会造成内存泄漏。注意,只有在使用父类创建子类对象时才会出现这种内存泄漏。
那么我们该怎么样防止这种内存泄漏呢----使用虚析构或者纯虚析构函数可以解决。(下文中的解决方法代码只是在父类Fruit1的析构函数上有差异,其他代码都与原代码相同)
使用虚析构解决内存泄露如下:
#include<iostream>
#include<string>
using namespace std;
class Fruit1
{
public:
Fruit1()
{
cout << "fruit1 ctor" << endl;
kind = "水果";
}
virtual ~Fruit1()
{
cout << "fruit1 dtor" << endl;
}
virtual void printkind()
{
cout << kind << endl;
}
private:
string kind;
};
class Apple :public Fruit1
{
public:
Apple()
{
//对堆中变量申请内存
a_name = new char();
//a_name =(char *) "我是水果类的子类";
a_kind = "苹果";
cout << "Apple ctor" << endl;
}
virtual ~Apple()//类中出现一个虚函数,在类大小中就相当于多出一个四字节的指针
{
//释放内存
cout << "Apple dtor" << endl;
delete a_name;;
}
virtual void printkind()
{
cout << a_kind<<" "<<a_name << endl;
}
private:
char* a_name;
string a_kind;
};
void test01()
{
Fruit1* apple = new Apple();
delete apple;
}
int main()
{
test01();
return 0;
}
运行结果如下
使用纯虚析构函数解决内存泄露问题:
#include<iostream>
#include<string>
using namespace std;
class Fruit1
{
public:
Fruit1()
{
cout << "fruit1 ctor" << endl;
kind = "水果";
}
virtual ~Fruit1() = 0;
virtual void printkind()
{
cout << kind << endl;
}
private:
string kind;
};
Fruit1:: ~Fruit1()
{
cout << "fruit1 dcot" << endl;
}
class Apple :public Fruit1
{
public:
Apple()
{
//对堆中变量申请内存
a_name = new char();
//a_name =(char *) "我是水果类的子类";
a_kind = "苹果";
cout << "Apple ctor" << endl;
}
virtual ~Apple()
{
//释放内存
cout << "Apple dtor" << endl;
delete a_name;;
}
virtual void printkind()
{
cout << a_kind<<" "<<a_name << endl;
}
private:
char* a_name;
string a_kind;
};
void test01()
{
Fruit1* apple = new Apple();
delete apple;
}
int main()
{
test01();
return 0;
}
运行结果如下:
在上面的代码中我们可以看到,因为使用纯虚函数后面必须是“=0”结尾,所以纯虚函数只能在类内声明,类外定义。在这种情况下可不可以只声明纯虚析构函数不实现呢?不可以,因为在后面释放子类占用的空间时,需要用到父类的析构函数,所以必须实现纯虚析构函数。