继承与动态内存分配今天这篇文章就来说说继承与动态内存分配。这里有一个问题。当我们的基类使用动态内存分配,重新定义赋值和拷贝构造函数时,这对派生类的实现会有什么影响呢?我们来看两种情况。派生类不使用new。假设基类使用动态内存分配:classbaseDMA{private:char*label;intrating;public:baseDMA(constchar*l="null",intr=0);baseDMA(constbaseDMA&rs);virtual~baseDMA();baseDMA&operator=(constbaseDMA&rs);};构造函数、析构函数、复制构造函数和重载赋值运算符都包含在该语句中。现在假设我们从baseDMA派生了lackDMA类,但后者不使用new:classlackDMA:publicbaseMDA{private:charcolor[40];public:...};问题是,我们是否应该为lackDMA类定义析构函数?函数、复制构造函数和赋值运算符呢?答案是不。首先是析构函数,这个很容易理解。如果我们不定义析构函数,编译器会自动定义一个什么都不做的默认析构函数。事实上,派生类的析构函数往往在执行完一些逻辑后,会调用基类的构造函数,因为lackDMA类中的成员不是通过new创建的,所以不需要额外的操作,所以默认的析构函数是合适的。相同的默认复制构造函数还执行非新创建成员的复制,因此对于颜色变量来说没问题。而在派生类中,默认的拷贝构造函数不仅会拷贝非new创建的成员,还会调用基类的拷贝构造函数拷贝父类成员的部分。因此,对于lackDMA的派生类,我们使用默认的拷贝构造函数是没有问题的。赋值也是一样,默认的赋值运算符会自动使用基类的赋值运算符给基类的成员赋值。派生类使用new我们来看看派生类中new的使用。classhasDMA:publicbaseMDA{private:char*style;public:...};在类hasDMA中,我们添加一个char*成员,需要用new来创建。在这种情况下,我们就不能使用默认函数了,必须定义显式析构函数、拷贝构造函数和赋值运算符,我们一一来看。第一个是析构函数。派生类的析构函数会自动调用基类的析构函数,所以我们只需要在析构函数中释放派生类特有的成员变量即可。hasDMA::~hasDMA(){delete[]style;}然后我们来看拷贝构造函数。由于派生类不能访问基类的私有成员,所以我们需要调用基类的拷贝构造函数。hasDMA::hasDMA(consthasDMA&hs):baseDMA(hs){style=newchar[std::strlen(hs.style)+1];std::strcpy(style,hs.style);}最后,赋值运算符,同样,由于派生类不能访问基类的私有成员,我们还需要使用基类的赋值运算符:hasDMA&hasDMA::operator(consthasDMA&hs){if(this==&hs)return*this;baseDMA::operator=(hs);delete[]style;style=newchar[std::strlen(hs.style)+1];std::strcpy(style,hs.style);return*this;}有语句看起来有点奇怪:baseDMA::operator=(hs);这是我们手动显式调用基类的赋值运算符,我们直接用等号赋值也是一样的效果:*this=hs;为什么不这样做呢?这是因为编译运算符在执行时,会默认调用子类的赋值运算符hasDMA::operator=,导致递归,死循环。因此,我们需要手动编写作用域解析器,说明这是调用的父类的赋值运算符,而不是派生类的运算符。这个比较晦涩,所以一定要注意。
