第一种情况:派生类不使用new
假设基类使用了动态内存分配:
class baseDMA
{
private:
char *label;
int rating;
public:
baseDMA(const char *lb = "null", int r = 0);
baseDMA(const baseDMA &rs);
virtual ~baseDMA();
baseDMA & operator=(const baseDMA &rs);
……
};
声明中包含了构造函数使用new时需要的特殊方法:析构函数,复制构造函数和重载赋值运算符。现从baseDMA中派生出lackDMA类,而后者不使用new,也未包含其他一些不常用的、需要特殊处理的设计特性。
class lacksDMA:public baseDMA
{
private:
char color[40];
public:
...
};
是否需要为lacksDMA类定义显示析构函数、复制构造函数和赋值运算符呢?不需要。
如果派生类没有定义析构函数,编译器将定义一个不执行任何操作的默认构造函数,派生类的默认构造函数执行一些操作后,调用基类析构函数,所以默认析构函数是合适的。
如果派生类没有定义复制构造函数,派生类默认复制构造函数执行成员复制,对于动态内存分配来说不合适,但是lacksDMA类的数据成员可以使用默认复制构造函数,在复制类成员或继承的类组件时,则调用该类的复制构造函数,所以lacksDMA类的默认复制构造函数使用显示baseDMA复制构造函数来复制lacksDMA对象的baseDMA部分,因此,默认复制构造函数也是合适的。
对于赋值来说,也是如此,默认的赋值构造函数将自动使用基类的赋值构造函数完成基类组件的赋值。
第二种情况:派生类使用new
class hasDMA:public baseDMA
{
private:
char *style;
public:
...
};
在这种情况下,必须为派生类定义显示析构函数、复制构造函数、赋值构造函数。
(1)析构函数:派生类析构函数自动调用基类的析构函数。因此hasDMA析构函数必须释放指针style管理的内存,并依赖于baseDMA的析构函数来释放指针label管理的内存。
baseDMA::~baseDMA()
{
delete [] label;
}
hasDMA::~hasDMA()
{
delete [] style;
}
(
2)复制构造函数:baseDMA复制构造函数遵循用于char数组的常规模式,使用strlen()来获悉存储空间、分配足够的内存(字节数+1)并使用strcpy()将原始字符串复制到目的地;
baseDMA::baseDMA(const baseDMA &rs)
{
label = new char[std::strlen(rs.label) + 1];
std::strcpy(label,rs.label);
rating = rs.rating;
}
hasDMA复制构造函数只能访问hasDMA的数据,因此它必须调用baseDMA复制构造函数来处理baseDMA共享的数据。
hasDMA::hasDMA(const hasDMA &hs):baseDMA(hs)
{
style = new char[std::strlen(hs.style) + 1];
std::strcpystyle,hs.style);
}
虽然baseDMA的构造函数并没有一个含hasDMA引用参数的,但是可以调用baseDMA自身的复制构造函数,因为基类的指针或引用可以指向派生类的指针或引用。
(3)赋值构造函数:
baseDMA赋值构造函数如下:
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;
}
派生类的显示赋值构造函数通过调用基类复制构造函数来完成派生类对基类数据对象的赋值。
hasDMA & hasDMA::operator=(const hasDMA &hs)
{
if(this == &hs)
return *this;
baseDMA::operator=(hs);//等价于*this = hs;
delete [] style;
style = new char[std::strlen(hs.style)+1];
std::strcpy(style,hs.style);
return *this;
}
综合以上三种情况,派生类的析构函数、复制构造函数、赋值构造函数分别由以下完成:1、对于析构函数,这是自动完成的;2、对于复制构造函数,是通过在初始化列表中调用基类复制构造函数完成;如果不这样做,将自动调用基类的默认构造函数;3、对于赋值构造函数,通过使用作用域解析运算符显示的调用基类的赋值构造函数来完成。
使用动态内存分配和友元函数的继承:
//dam.h
#ifndef DMA_H_
#define DMA_H_
#include <iostream>
using namespace std;
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 = "black",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 *s = "none",const char *l = "null",int r = 0);
hasDMA(const char *s,const baseDMA &rs);
hasDMA(const hasDMA &hs);
hasDMA &operator=(const hasDMA &rs);
~hasDMA();
friend std::ostream &operator<<(std::ostream &os,const hasDMA &rs);
};
#endif
//dam.cpp
#include <cstring>
#include "dma.h"
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 << endl;
os << "Rating:" << rs.rating << 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 &ls)
{
os << (const baseDMA &)ls;
os << "Color:" << ls.color << endl;
return os;
}
hasDMA::hasDMA(const char *s,const char *l,int r):baseDMA(l,r)
{
style = new char[std::strlen(s)+1];
std::strcpy(style,s);
}
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 << endl;
return os;
}
//usedma.cpp
#include <iostream>
#include "dma.h"
#include <stdlib.h>
using namespace std;
int main()
{
baseDMA shirt("Portabelly",8);
lacksDMA balloon("red","Blimpo",4);
hasDMA map("Mercator","Buffalo Keys",5);
cout << "Displaying baseDMA object:\n";
cout << shirt << endl;
cout << "Displaying lacksDMA object:\n";
cout << balloon << endl;
cout << "Displaying hasDMA object:\n";
cout << map << endl;
lacksDMA balloon2(balloon);
cout << "Result of lacksDMA copy:\n";
cout << balloon2 << endl;
hasDMA map2;
map2 = map;
cout << "Result of hasDMA assignment:\n";
cout << map2 << endl;
system("pause");
return 0;
}
因为友元函数不是类的成员函数,因此不能使用作用域解析运算符来指出使用哪个operator<<()函数,这个问题的解决方法是使用强制类型转换,以便匹配原始时能够选择正确的函数,因此代码将参数const hasDMA &转换成类型为const baseDMA &的参数。
std::ostream &operator<<(std::ostream &os, const hasDMA &hs)
{
os << (const baseDMA &) hs;
os << "Style:" << hs.style << endl;
return os;
}