关于继承时用new和不用new动态内存分配的问题
派生类中不用动态内存分配
- 派生类还要重新定义析构函数,复制构造函数和复制运算符吗?
- 对于析构函数,当派生类成员不进行其他操作时是不必要的。因为没有使用动态内存分配,所以可以直接调用默认析构函数。
- 对于复制构造函数,不需要。因为不使用动态内存分配的话就不用我们手动delete,对于基类部分会自动调用基类的复制构造函数(可能是显示的也可能是默认的),对于派生类部分会使用默认复制构造函数,因为没有使用动态内存分配,所以默认复制构造函数的机制是适合的。
- 对于赋值运算符,不需要。原因和第二点是一样的,基类部分会使用基类的复制构造函数,派生类使用默认的。
派生类中使用动态内存分配
- 派生类中还要重新定义析构函数,复制构造函数和复制运算符吗?
- 对于析构函数,需要。因为派生类中使用了动态内存分配,所以要手动delete。对于派生类析构函数,可以这样定义:
baseDMA::~baseDMA() { delete [] Variable1; //释放使用了动态内存分配的基类变量 } hasDMA::~hasDMA() { delete [] Variable2; //释放使用了动态内存分配的派生类变量 }
因为创建派生类对象前会先创建一个基类对象,也就是说先调用基类构造函数再调用派生类构造函数,所以当释放派生类对象时,会先调用派生类析构函数在调用基类析构函数。
- 对于复制构造函数,需要。因为在派生类使用了动态内存分配,而默认的复制构造函数只是浅复制,就是其实只是复制一个地址,而你使用了new来动态内存分配,当你原来的对象被释放后那个地址中的数据也被释放了,这时候你再用被复制的对象时就会出问题,所以默认的复制构造函数不适合。所以要重新定义复制构造函数,重新定义的派生类复制构造函数还要调用基类的复制构造函数,因为构造函数概念上是会在执行括号中的内容前就创建好对象的,所以要先创建基类对象的话要使用成员初始化列表来调用基类复制构造函数来创建基类对象。而派生类复制构造函数的参数部分可以使用派生类的引用,派生类复制构造函数将使用参数的基类部分来构造派生类对象的基类部分。
- 对于赋值运算符,需要。原因和第二点一样。然后派生类的复制运算符也需要调用基类的复制运算符,不过它要在函数里面显式调用,因为派生类的赋值运算符和基类的复制运算符的名称是一样的,所以在函数里面可以用作用域符来声明调用基类的赋值运算符来完成基类部分的复制。代码类似于:
hasDMA & hasDMA operator=(const hasDMA & hs) { if ( this == &hs ) return *this; baseDMA::operator=(hs);//使用作用域符调用基类赋值运算符来复制基类部分 ........ ........ ........ }
注意:所有重新定义的函数中对于复制原来的数据都要进行深复制而不是浅复制,就是要创建一个副本要重新开辟个内存来放数据,而不是只复制地址。
派生类如何调用基类友元函数
因为友元函数不是成员函数,所以无法使用作用域符。但是我们可以使用强制类型转换。比如我们重载了输出流运算符并在基类中声明为友元函数,使其可以后面接基类对象时将会输出基类的成员,然后我们又要再次重载使其能输出派生类的成员,并声明为派生类的友元函数。因为派生类对象的成员包括了基类的成员,所以也应该要输出基类部分的成员,而对于私有部分,如果没有定义公有函数来输出的话,我们还可以通过基类的输出流友元函数来进行输出,而友元函数不是成员函数,所以不能用作用域符,要用强制转换类型。
ostream & operator<<(ostream & os,const hasDMA & hs)
{
os << (const baseDMA &) hs;//将派生类对象强制转换为基类引用来调用基类重载的函数
os<<"派生类部分";//然后再输出派生类部分
return os;
}