文章目录
当继承和动态内存分配搅和在一起,会有什么火花
当基类没用new和delete(动态内存分配)
继承和动态内存分配搅和在一起时,需要分四种情况,基类不用动态内存分配的话,派生类用或者不用,都是他自己的事,对基类并无影响,所以不予讨论。
截止目前,对继承的探讨中,基类一直都没用new和delete,即动态内存分配。也就是说,之前的基类都没有指针成员。这时候派生类用不用动态内存分配都随他们自己高兴,因为要用动态内存分配也是把new和delete用到他们自己新添的数据成员身上,和基类并无干系。
当基类用了new和delete(动态内存分配)
这是本文的主角了。
但是基类如果用了动态内存分配,那么基类最好是定义一个显式的复制构造函数和赋值运算符成员函数,因为使用动态内存分配就说明这个类一定有指针成员,以便于存储动态分配内存的地址,有指针成员的话,如果要用已有的对象初始化一个对象,或者对象之间赋值,就会分别调用复制构造函数和赋值运算符函数。前面说了,如果自己不显式定义这两个函数,编译器就会上手,自动定义两个默认版本,二者的默认版本都是使用成员复制,即浅复制,浅复制会按照成员的数据类型来判断使用什么复制方法,比如基本内置类型int, long就直接赋值,string类等类对象成员就调用他们类的复制构造函数进行复制。所以指针类型也是被像简单类型那样,直接赋值,于是就只是复制了地址的值,并没有复制真正的内容。于是被初始化的新对象或者被赋值的对象就会和源对象的指针成员指向同一各地址处的内容,而不是各自独占一个副本,只要其中任何一人先行更改指针指向的内容或被删除(过期),则另一人也随之玩完。
说了好长,总之有动态内存分配就一定有指针成员,有指针成员就一定要自己用深复制显式写复制构造函数和赋值运算符函数。
所以基类既然要用动态内存分配,那就必须用深复制显式写复制构造函数和赋值运算符函数。
派生类不用动态内存分配
解答下面三个问题的关键:基类对象实际上是派生类对象的一部分,派生类对象包含了基类对象。甚至基类对象有点像派生类对象的一个成员,但是又是个没名字的成员,因为复制构造函数复制基类的数据成员时会调用基类的复制构造函数。
派生类是否需要显示定义析构函数: no
自己定义析构函数的目的就是去释放用new分配内存的成员,前面说过,只要没有用动态内存分配,那么根本不用写析构函数,编译器提供的就足够了,就算自己非要写,也是空圆括号空花括号了事。因为类的对象的成员们都存在栈中,程序结束都会自动释放。
那么现在明确说了基类用了动态分配而派生类没用,派生类是否还是可以不定义析构函数呢?答案是:可以。
因为派生类不定义析构函数,会用编译器的默认析构函数,默认析构函数什么都不做,除了调用基类的析构函数。基类的析构函数就会释放派生类对象中基类对象的那部分用了动态内存分配的,于是问题解决了。
派生类是否需要显示定义复制构造函数: no
不用。
一样的思路。派生类不定义,就使用编译器的默认复制构造函数,由于成员复制会根据成员的数据类型判断怎么复制,派生类对象中的基类对象成员就会通过调用基类的复制构造函数来复制,所以不会出问题。
派生类是否需要显示定义赋值运算符函数: no
同上。
派生类也用动态内存分配
解释了上面的情况,下面的答案也就有了。
派生类是否需要显示定义析构函数: yes
需要自定义析构函数以delete派生类新增加的指针成员,即要用new分配内存的。不管是不是在构造函数中动用new去分配内存,析构函数都要delete。(有时候没在构造函数new,却在其他地方new了,不要简单一看构造函数没有new就不写析构函数了)
派生类是否需要显示定义复制构造函数: yes
需要自定义复制构造函数以深复制派生类新增加的指针成员
派生类是否需要显示定义赋值运算符函数: yes
需要自定义赋值运算符函数以深复制派生类新增加的指针成员
示例
代码
//baseDMA.h
#ifndef BASEDMA_H_
#define BASEDMA_H_
#include <iostream>
class baseDMA{
private:
char * label;
int rating;
public:
baseDMA(const char * s = "null", int r = 0);
baseDMA(const baseDMA & bd);//复制构造函数
baseDMA & operator=(const baseDMA & bd);//赋值运算符函数
virtual ~baseDMA();
friend std::ostream & operator<<(std::ostream & os, const baseDMA & bd);//输出运算符,友元函数
};
//不用动态内存分配的派生类
class lacksDMA : public baseDMA
{
private:
enum {
LEN = 40};
char color[LEN];
public:
lacksDMA(const char * s = "null", int r = 0, const char * c = "none");
lacksDMA(const baseDMA & bd, const char * c = "none");
//友元函数并不属于类,所以不能被继承,必须自己写哦
friend std::ostream & operator<<(std::ostream & os, const lacksDMA & ld);
};
//用动态内存分配的派生类
class hasDMA : public baseDMA
{
private:
char *style;
public:
hasDMA(const char * s =