(一四二)继承和动态内存分配
来源:互联网 发布:会计网络课程 编辑:程序博客网 时间:2024/05/21 01:28
当基类、派生类用,或者不用动态内存时,共有四种情况:
(注意,前提是基类的动态内存分配相关函数符合常规使用动态内存的要求)
情况一:基类 使用 动态内存分配、派生类新增数据成员 不使用 动态内存分配
假如基类使用动态内存分配(new),其必然设定①构造函数、②复制构造函数、③赋值运算符、④析构函数。
又知,派生类的构造函数(包括默认构造函数),需要调用基类的构造函数。
那么基类的数据成员若使new,则已经没问题。
派生类新增的数据成员,由于不使用new,因此可以按值传递。
①对于派生类构造函数:直接将参数赋值对应的数据成员——ok;
②对于派生类复制构造函数,使用默认的复制构造函数(因此对于派生类新增数据对象使用按值传递,对于基类的数据对象使用基类的复制构造函数)——ok;
③对于赋值运算符来说,使用默认的赋值运算符,是按值传递(因此对派生类新增的数据成员,按值传递,对基类数据对象,使用基类的赋值运算符函数),如果有特殊需求,则显式调用基类的赋值运算符(A::operator=(b);//显式调用基类赋值运算符函数),然后对派生类的数据成员进行处理(③可以参考情况三的赋值运算符处理)——ok;
④对于析构函数来说,会先调用派生类的析构函数,然后调用基类的析构函数——ok。
因此:假如派生类新增数据成员不使用动态内存的话,如无特别需求,可以无需特别设置。
使用默认的构造函数、复制构造函数、赋值运算符、析构函数即可。
如代码:
class A//基类{char*name;public:A(const char*q)//构造函数{name = new char[strlen(q) + 1];strcpy_s(name, strlen(q) + 1, q); }A(const A&a)//复制构造函数{ name = new char[strlen(a.name) + 1];strcpy_s(name, strlen(a.name) + 1, a.name);}virtual ~A() { delete[]name; }//析构函数A&operator=(const A&a)//赋值运算符{if (this == &a)return *this;//为防止自己赋值自己delete[]name;name = new char[strlen(a.name) + 1];strcpy_s(name, strlen(a.name) + 1, a.name);return *this;}friend std::ostream& operator<<(std::ostream&os, const A&b){os << b.name;return os;}};class B :public A//基类的派生类{int id;public:B(const char*a, int q) :A(a)//构造函数{id = q;}B(const B&b) :A(b)//复制构造函数,可省略,使用默认复制构造函数{id = b.id;}B&operator=(const B&b)//赋值运算符,可省略,使用默认的赋值运算符{if (this == &b)return *this;//为防止自己赋值自己A::operator=(b);//显式调用基类赋值运算符函数id = b.id + 5;//为了区分而修改return *this;}friend std::ostream& operator<<(std::ostream&os, const B&b){os << A(b) << "," << b.id;return os;}};
情况二:基类 不使用 动态内存分配、派生类新增数据成员 不使用 动态内存分配
和情况一并没有什么区别。如无特殊需求,使用默认的复制构造函数、赋值运算符即可。
情况三:基类 使用 动态内存分配、派生类新增数据成员 使用 动态内存分配
由于派生类新增数据成员使用动态内存分配,那么显然,不能使用默认构造函数、默认的复制构造函数、默认的赋值运算符、默认的析构函数了(否则无法形成new和delete的对应)。
①构造函数:
首先,构造函数需要调用基类的构造函数,是毫无疑问的。因此,基类的动态内存分配ok;
其次,对于派生类新增的数据成员,对于需要使用动态内存的,使用new运算符来分配内存。不使用动态内存的,常规处理,因此ok;
②复制构造函数:
首先,复制构造函数需要调用基类的复制构造函数。将派生类对象作为参数传递给基类初始化列表,由于基类引用可以指向派生类对象,因此可以成功初始化派生类的基类部分。
然后,对派生类新增的部分,对于需要使用动态内存的,使用new运算符来分配内存。不使用动态内存的,常规处理,因此ok;
③赋值运算符:
首先,需要显式的调用基类的赋值运算符(A::operator=(b);//显式调用基类赋值运算符函数),以使得基类的部分被成功赋值;
其次,对于派生类部分,按照正常方式处理使用动态内存的数据成员(delete后再new),和不使用动态内存的数据成员。
最后,注意在函数的开始部分,添加防止自己赋值给自己的代码。
④析构函数:
由于派生类的析构函数,会自动调用基类的析构函数,因此,只对派生类新增的数据成员进行delete释放内存处理。
如代码(只修改了派生类B):
class B :public A//基类的派生类{char* id;public:B(const char*a, char*q) :A(a)//构造函数{id=new char[strlen(q)+1];strcpy_s(id, strlen(q) + 1, q);}B(const B&b) :A(b)//复制构造函数{id = new char[strlen(b.id) + 1];strcpy_s(id, strlen(b.id) + 1, b.id);}~B()//析构函数,处理派生类新增数据成员{delete[]id;}B&operator=(const B&b)//赋值运算符{if (this == &b)return *this;//为防止自己赋值自己A::operator=(b);//显式调用基类赋值运算符函数delete[]id;//需要先deleteid = new char[strlen(b.id) + 1];strcpy_s(id, strlen(b.id) + 1, b.id);return *this;}friend std::ostream& operator<<(std::ostream&os, const B&b){os << A(b) << "," << b.id;return os;}};
情况四:基类 不使用 动态内存分配、派生类新增数据成员 使用 动态内存分配
和情况三的办法一样(因为情况三使用基类的各种方法来处理基类部分的数据)。
总结:
①我忘了给基类加析构函数;
②我加了析构函数,忘了给析构函数加关键字virtual变成虚函数。
对于友元函数:
如果想在派生类的友元函数中调用基类的友元函数,那么应该使用强制类型转换,即 基类名(派生类对象) ,即可使用,如A(b)
也可以这样:(const 基类名&)派生类对象 表示强制转换派生类对象为const基类类型。
- (一四二)继承和动态内存分配
- 继承和动态内存分配
- C++ 学习(继承和动态内存分配)
- C++之继承和动态内存分配
- 继承 之 动态内存分配
- 继承 之 动态内存分配
- 继承和动态内存分配(C++ Primer Plus 第十三章)
- 继承和动态内存分配(C++ Primer Plus 第十三章)
- 静态内存分配和 动态内存分配
- C++中继承与动态内存分配
- C++继承关系中的动态内存分配
- 动态内存分配和指针
- 动态内存分配和类
- 动态内存分配和释放
- 类和动态内存分配
- 类和动态内存分配
- 类和动态内存分配
- 类和动态内存分配
- 一些工具和常用配置
- Ubuntu14.04下如何配置固定IP
- (一四〇)访问控制:protected
- (一四一)抽象基类
- poj 2182 -Lost Cows -线段树-还原逆序数组
- (一四二)继承和动态内存分配
- 205. Isomorphic Strings
- mysql免安装版的配置
- 一个多层感知机C++的简单实现
- Windows7 过渡 Windows10 的修改
- matlab 设置图片大小,保存eps格式
- 数据结构(1)--线性表顺序表的主要操作的实现
- [2023]:求平均成绩
- (一四三)类设计回顾