前言
本篇继续学习类继承中的动态内存问题。
C++类中如果创建了动态内存,则需要手动提供复制构造函数,析构函数和重载赋值运算符,以防止内存泄漏和double free问题。
基类的动态内存
之前的博客中提到,如果类内分配了动态内存,则必须手动提供复制构造函数,析构函数和重载赋值运算符。
在基类中new 一块动态内存的示例如下:
class Base{
private:
int* pa_;
public:
Base();
Base(const Base&);
~Base();
Base& operator= (const Base&);
};
Base::Base(){
std::cout << "base default constructor" << std::endl;
pa_ = new int [5];
pa_[0] = 0;
pa_[1] = 1;
pa_[2] = 2;
pa_[3] = 3;
pa_[4] = 4;
}
Base::Base(const Base& obj){
std::cout << "base copy constructor" << std::endl;
int len = 5;
pa_ = new int [len];
for(int i=0;i < len; i++)
{
pa_[i] = (obj.pa_)[i];
std::cout << pa_[i] << " ";
}
std::cout << std::endl;
}
Base::~Base(){
std::cout << "base destructor" << std::endl;
delete [] pa_;
}
Base& Base::operator= (const Base& obj){
std::cout << "base assign constructor" << std::endl;
if(this == &obj)
return *this;
delete [] pa_;
int len = 5;
pa_ = new int [len];
for(int i=0;i < len; i++)
{
pa_[i] = (obj.pa_)[i];
std::cout << pa_[i] << " ";
}
std::cout << std::endl;
return *this;
}
基类的实现与普通类使用动态内存的实现一致。
派生类的动态内存
如果基类使用了动态内存,那么派生类的实现将有两种情况。
派生类不使用动态内存
如果派生类不使用动态内存,则可以不手动提供派生类的析构函数,复制构造函数和赋值运算符。
派生类默认的析构函数将调用基类的析构函数释放基类分配的动态内存。
派生类默认的复制构造函数将首先调用基类的复制构造函数创建基类对象,然后再创建派生类对象。
派生类默认的赋值运算符将自动使用基类重载的赋值运算符为基类对象赋值,然后再为派生类对象赋值。
派生类对象的部分没有涉及到动态内存,因此默认上述函数的默认形式都没有问题。
派生类使用动态内存
此时,派生类需要手动提供析构函数,复制构造函数和重载赋值运算符。
派生类的析构函数会自动调用基类的析构函数,因此只需要将派生类部分清理即可。
派生类的复制构造函数应当在成员初始化列表中显式调用基类的复制构造函数,然后再初始化派生类成员。
派生类的赋值运算符应当显式调用基类的赋值运算符。
示例如下:
class Derived : public Base{
private:
int* pb_;
public:
Derived();
Derived(const Derived&);
~Derived();
Derived& operator= (const Derived&);
};
Derived::Derived(): Base(){
std::cout << "derived default constructor" << std::endl;
pb_ = new int [5];
pb_[0] = 10;
pb_[1] = 11;
pb_[2] = 12;
pb_[3] = 13;
pb_[4] = 14;
}
Derived::~Derived(){
std::cout << "derived destructor" << std::endl;
delete [] pb_;
}
Derived::Derived(const Derived& obj): Base(obj){
std::cout << "derived copy constructor" << std::endl;
int len = 5;
pb_ = new int [len];
for(int i=0; i < 5; i++)
{
pb_[i] = (obj.pb_)[i];
std::cout << pb_[i] << " ";
}
std::cout << std::endl;
}
Derived& Derived::operator= (const Derived& obj){
std::cout << "derived assign constructor" << std::endl;
if(this == &obj)
return *this;
Base::operator= (obj);
delete [] pb_;
int len = 5;
pb_ = new int [len];
for(int i=0; i < len; i++)
{
pb_[i] = (obj.pb_)[i];
std::cout << pb_[i] << " ";
}
std::cout << std::endl;
return *this;
}
~Derived() 派生类析构函数隐含调用了的基类析构函数。
Derived::Derived(const Derived& obj) 派生类复制构造函数在成员初始化列表中首先创建了基类对象。
Derived& Derived::operator= (const Derived& obj) 派生类赋值运算符中,通过Base::operator=(obj); 显式调用了基类的赋值运算符。
最终结果:
base default constructor
derived default constructor
base copy constructor
0 1 2 3 4
derived copy constructor
10 11 12 13 14
derived assign constructor
base assign constructor
0 1 2 3 4
10 11 12 13 14
derived destructor
base destructor
derived destructor
base destructor
|