C++ 继承和动态内存分配
2017-08-03 00:00
204 查看
摘自《c++ Primer Plus》第6版 13.7
如果基类使用的动态内存分配,并重新定义了赋值和复制构造函数,这将怎样影响派生类的实现?
默认析构函数合适吗?合适,因为派生类没有执行任何特殊的new操作,所以先调用派生类的默认析构函数,再调用基类的析构函数,很ok。
复制构造函数合适吗?合适。以前介绍过,默认复制构造函数执行的成员浅拷贝对动态内存分配是不合适,但对于没有new操作的派生类是合适的。对于派生类复制,派生类的复制构造函数使用显示的基类构造复制构造函数来赋值派生类对象的基类部分数据。因此,默认复制构造函数对于新的派生类成员是合适的。
同理,默认赋值运算符也合适。
派生类对象的这些属性也适用于包含其他类对象成员的类。例如实现
析构函数:
复制构造函数:
重载赋值运算符:
如果基类使用的动态内存分配,并重新定义了赋值和复制构造函数,这将怎样影响派生类的实现?
情况1:派生类不使用new
假设基类在构造函数中使用new,析构函数、复制构造函数和重载赋值运算符也做了相应处理。现在,从base类派生出一个类,这个派生类不使用new,只是包含一些新的数据而已。那么就不需要为派生类定义显式析构函数、复制构造函数、赋值运算符。默认析构函数合适吗?合适,因为派生类没有执行任何特殊的new操作,所以先调用派生类的默认析构函数,再调用基类的析构函数,很ok。
复制构造函数合适吗?合适。以前介绍过,默认复制构造函数执行的成员浅拷贝对动态内存分配是不合适,但对于没有new操作的派生类是合适的。对于派生类复制,派生类的复制构造函数使用显示的基类构造复制构造函数来赋值派生类对象的基类部分数据。因此,默认复制构造函数对于新的派生类成员是合适的。
同理,默认赋值运算符也合适。
派生类对象的这些属性也适用于包含其他类对象成员的类。例如实现
Stock类时,可以用
string类而不是
char *来存储公司名称。众所周知
string类有采用动态内存分配,而
Stock的默认构造函数不会产生问题,我们现在知道了原因——默认构造函数使用
string的复制构造函数来复制
company成员,
Stock的默认赋值运算符使用
string的赋值运算符来给
company对象赋值,而
Stock的析构函数会自动调用
string类的析构函数。
情况2:派生类使用new
在这种情况下,必须为派生类定义显示析构函数、复制构造函数、赋值运算符。//包含动态内存分配的类继承 #ifndef _DMA_H_ #define _DMA_H_ #include<iostream> class baseDMA { private: char *label; int rating; public: baseDMA(const char * l = "null", int r = 0); baseDMA(const baseDMA & rs); virtual ~baseDMA(); baseDMA & operator=(const baseDMA & rs); friend std::ostream & operator<<(std::ostream & os, const baseDMA & rs); }; class lacksDMA :public baseDMA { private: enum { COL_LEN = 40 }; char color[COL_LEN]; public: lacksDMA(const char * c = "blank", const char * l = "null", int r = 0); lacksDMA(const char * c, const baseDMA & rs); friend std::ostream & operator<<(std::ostream & os, const lacksDMA & rs); }; class hasDMA :public baseDMA { private: char * style; public: hasDMA(const char * c = "none", const char * l = "null", int r = 0); hasDMA(const char * s, const baseDMA & rs); hasDMA(const hasDMA & hs); ~hasDMA(); hasDMA & operator=(const hasDMA & hs); friend std::ostream & operator<<(std::ostream & os, const hasDMA & hs); }; #endif #include<cstring> baseDMA::baseDMA(const char *l, int r) { label = new char[std::strlen(l) + 1]; std::strcpy(label, l); rating = r; } baseDMA::baseDMA(const baseDMA & rs) { label = new char[std::strlen(rs.label) + 1]; std::strcpy(label, rs.label); rating = rs.rating; } baseDMA::~baseDMA() { delete[] label; } baseDMA & baseDMA::operator=(const baseDMA & rs) { if (this == &rs) return *this; delete[]label; label = new char[std::strlen(rs.label) + 1]; std::strcpy(label, rs.label); rating = rs.rating; return *this; } std::ostream & operator<<(std::ostream & os, const baseDMA &rs) { os << "Label: " << rs.label << std::endl; os << "Rating: " << rs.rating << std::endl; return os; } lacksDMA::lacksDMA(const char * c, const char * l, int r) :baseDMA(l,r) { std::strncpy(color, c, 39); color[39] = '\0'; } lacksDMA::lacksDMA(const char * c, const baseDMA & rs) :baseDMA(rs) { std::strncpy(color, c, COL_LEN - 1); color[COL_LEN - 1] = '\0'; } std::ostream & operator<<(std::ostream & os, const lacksDMA & rs) { os << (const baseDMA &)rs; os << "Color: " << rs.color << std::endl; return os; } hasDMA::hasDMA(const char * c, const char * l, int r) :baseDMA(l, r) { style = new char[std::strlen(c) + 1]; std::strcpy(style, c); } hasDMA::hasDMA(const char * s, const baseDMA & rs) :baseDMA(rs) { style = new char[std::strlen(s) + 1]; std::strcpy(style, s); } hasDMA::hasDMA(const hasDMA & hs) :baseDMA(hs) { style = new char[std::strlen(hs.style) + 1]; std::strcpy(style, hs.style); } hasDMA::~hasDMA() { delete[]style; } hasDMA & hasDMA::operator=(const hasDMA & hs) { if (this == &hs) return *this; baseDMA::operator=(hs); delete[]style; style = new char[std::strlen(hs.style) + 1]; std::strcpy(style, hs.style); return *this; } std::ostream & operator<<(std::ostream & os, const hasDMA & hs) { os << (const baseDMA &)hs; os << "Style: " << hs.style << std::endl; return os; }
上述代码注意点:
基类
基类使用了动态内存分配,所以声明包含了使用new时所需要的特殊方法:析构函数:
virtual ~baseDMA();
复制构造函数:
baseDMA(const baseDMA & rs);
重载赋值运算符:
baseDMA & operator=(const baseDMA & rs);
基类使用new,派生类不使用new
lacksDMA类,没有使用动态内存分配,所以无需提供特殊方法。基类/派生类析构函数
这里有两个派生类,派生类析构函数会自动调用基类的析构函数,所以各自的职责就是对派生类构造函数执行的工作进行清理。hasDMA类释放
style,
baseDMA类释放
label。
派生类如何访问基类的友元?
作为派生类的友元,<<重载函数,不是基类的友元,那怎么访问基类成员
label和
rating呢?答案是使用
baseDMA::operator<<()。因为友元不是成员函数,不能通过作用域解析运算符来指示要使用哪个函数,所以这里的处理方法是使用强制类型转换,以便通过匹配正确原型来使用正确的函数。
复制构造函数也用上了成员初始化列表
hasDMA类的复制构造函数只能访问派生类的数据,所以它必须调用
baseDMA的复制构造函数来处理共享的基类数据.
特别需要注意的一点:派生类的赋值运算符
baseDMA::operator=(hs);实际上该语句的语义是:
*this = hs;也就是说,使用基类的赋值运算符,来复制派生类对象的基类部分的数据。为使用了new的派生类设计赋值运算符时,必须给类的每个成员都提供赋值运算符,而不仅仅是新的。
原则:
当基类和派生类都采用动态内存分配时,派生类的析构函数、复制构造函数、赋值运算符,都必须使用相应的基类方法来处理基类元素。相关文章推荐
- C++之继承和动态内存分配
- C++中继承与动态内存分配
- C++ 学习(继承和动态内存分配)
- C++之继承和动态内存分配
- 关于C++赋值运算符能不能继承问题
- C++接口继承规则
- C++程序之继承
- C++中类的继承访问特性
- 《C++ Primer Plus》14.2 私有继承 学习笔记
- c++ 类继承中父类和子类的数据关系
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++的继承关系
- C++对象模型5--多继承下的对象模型
- c++继承中的内存布局(一)
- C++继承与派生上机记录
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++知识点--继承
- C++菱形继承
- C++中虚拟继承问题
- C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别