一、派生类中不使用new
在派生类中不使用new意味着,派生类中新增加的成员没有指针类型的成员。
(1)派生类中是否需要显式的定义析构函数?
不需要。因为在派生类中没有指针成员,所以在派生类对象生命周期结束时,使用默认的派生类的析构函数也是可以释放掉派生类中新增加的成员,然后调用基类的析构函数完成对派生类中继承下来的成员的清理工作。
(2)派生类中是否需要显式的定义拷贝构造函数?
不需要。因为在派生类中没有指针,所以在拷贝的过程中,先显式的调用基类的拷贝构造函数,完成对派生类中从基类继承的成员的复制,然后使用派生类的默认拷贝构造函数完成对派生类中新增成员的复制。
(3)派生类中是否需要显式的定义赋值运算符重载函数?
不需要。因为在派生类中没有指针,所以在赋值的过程中,先显式的调用基类的赋值运算符重载函数,完成对派生类中从基类继承的成员的赋值,然后使用派生类的默认赋值运算符重载函数完成对派生类中新增成员的赋值。
二、派生类中使用new
假如在基类中使用了new,并且在派生类中同样使用了new。
//基类的定义
class Base
{
private:
char* Base_str;
int len;
public:
Base(char* _str=nullptr,int _len);
virtual ~Base()
Base(const Base& b);
Base& operator =(const Base& b);
......
};
//派生类定义
class Derived:public Base
{
private:
char* Derived_str;
......
};
(1)派生类中是否需要显式的定义析构函数?
需要。因为在派生类中使用了new,所以此时如果我们仅仅调用默认的析构函数,那么就无法将Derive_str
所指向的空间释放掉。因此我们需要自己显式的定义析构函数。如下所示:
//基类的析构
Base::~Base()
{
delete[] Base_str;
}
//派生类的析构
Derived::~Derived()
{
delete[] Derived_str;
}
这样,当执行完派生类的析构后,再调用基类的析构就能完成对整个派生类对象的清理工作。
注意:派生类析构函数只是对新增加的成员进行清理工作,而从基类中继承下来的成员则由基类的析构进行。
(2)派生类中是否显式的定义拷贝构造?
需要。
//基类的拷贝构造
Bsse::Base(const Base& b)
{
Base_str=new char[strlen(b.Base_str)+1];
strcpy(Base_str,b.Base_str);
len=b.len;
}
//派生类的拷贝构造
Derived::Derived(const Derived& d):Base(d)
{
Derived_str=new char[strlen(b.Derived_str)+1];
strcpy(Derived_str,b.Derivede_str);
}
先调用基类的拷贝构造完成派生类中基类成员的拷贝,再调用派
生类的拷贝构造完成对派生类中新增成员的拷贝。
注意:
成员初始化列表将Derived
的引用传递给了Base
的构造函数。但是在Base类中没有一个这样可以接受Derived
引用类型的构造函数,那么需不需定义一个专门接收这种类型的构造函数呢?答案是不需要,因为拷贝构造函数,有一个Base
类型的引用,而派生类对象可以赋值给基类引用。因此Derived
引用将作为实参传递给拷贝构造函数,然后利用拷贝构造函数构造新对象的基类部分。
(3)派生类中是否显式的定义赋值运算符重载函数?
需要。
//基类的赋值运算符重载函数
Base& operator =(const Base& b)
{
if(this==&b)
return *this;
delete[] Base_str;
Base_str=new char[strlen(b.Base_str)+1];
strcpy(Base_str,b.Base_str);
len=b.len;
return *this;
}
//派生类的赋值运算符重载函数
Derived& operator =(const Derived& d)
{
if(this==&d)
return *this;
Base::operator=(d); //调用了基类的赋值函数
delete[] Derived_str;
Derived_str=new char[strlen(d.Derived_str)+1];
strcpy(Derived,d.Derived);
return *this;
}
总结
如果在派生类中没有使用new,那么可以不用显式的定义析构函数、拷贝构造函数、赋值运算符重载函数。
如果在派生类中使用了new,那么必须显式的定义相应的析构函数、拷贝构造函数、赋值运算符重载函数。对于析构函数来说,由系统自动调用完成;对于拷贝构造函数来说,通过成员初始化列表中调用基类的拷贝构造函数来完成的;对于赋值运算符重载函数来说,是通过作用域解析运算符显式的调用基类的赋值运算符重载函数来完成的。