在这里总结下C++类继承的一些东西(针对于我自己,也是我自己的理解)

关于C++中的派生类和基类的关系就总结下来就是:

  1. 派生类继承了基类的部分方法
  2. 有些方法是不能被继承的,例如构造函数、析构函数、赋值运算符
  3. 基类的数据成员也会继承下来,但是private成员是不能够被派生类直接访问的,需要通过基类的共有方法来访问,protected数据是可以向public数据一样被派生类通过名称空间解析运算符::来访问。
我就把派生类想象成了一个包含基类的类,基类的小秘密不对外公开,想要改变小秘密的内容要使用基类自己的方法(比如基类的构造函数,基类的赋值运算符函数等),总之代码写下来感觉,其实基类在派生类中还是很独立的
在派生类构造派生类之前先调用基类的构造函数初始化基类组件,在调用派生类析构函数时会在析构派生类对象之后调用基类的析构函数释放基类组件。顺序保持一致。

下面总结下要注意(或者说我后面可能会忘记)的一些东西

重新定义的赋值运算符

赋值运算符是不能够被继承的,原因很简单:赋值运算符的重载函数返回的是本对象的类型值的引用,派生类和基类的类型并不相同,也就没有什么继承的必要了。因此要重新定义赋值运算符。
在重载赋值运算符时要显式的调用基类的赋值运算符函数来给基类的私有成员赋值,例如:

1
2
3
4
5
6
7
8
9
10
11
hasDMA & hasDMA::operator=(const hasDMA & hs)
{
if (this == &hs)
return *this;
ABC::operator=(hs);
delete [] style;
style = new char [std::strlen(hs.style) + 1];
std::strcpy(style, hs.style);

return *this;
}

通过成员初始化列表初始化基类继承来的成员数据

因为基类就好像是派生类内部的一个独立的个体,因此要给他里面的私有成员赋值还是要调用基类自己的构造函数来,例如:

1
2
3
4
5
6
hasDMA::hasDMA(const char * s, const char * l, int r)
: ABC(l, r)
{
style = new char [std::strlen(s) + 1];
std::strcpy(style, s);
}

若是复制构造函数,则直接使用派生类对象初始化基类成员变量,因为是可以把派生类赋值给基类的,例如:
1
2
3
4
5
6
hasDMA::hasDMA(const char * s, const ABC & c)
: ABC(c)
{
style = new char [std::strlen(s) + 1];
std::strcpy(style, s);
}


是否要重新定义构造函数,复制运算符函数,析构函数这些不能被继承的成员方法?

  1. 即使派生类没有添加任何数据成员,仍需要构造函数,可以为空但必须存在,例如:
    1
    2
    3
    baseDMA::baseDMA(const char * c, int r) : ABC(l, r)
    {
    }
  2. 如果在派生类中没有重新定义赋值运算符函数和析构函数则会使用编译器自动生成的这些函数,
    默认的赋值就是将数据成员简单的复制,即浅复制
    若类中有其他对象作为数据成员,则会使用这些对象的赋值运算符函数来进行复制。
    派生类中的基类组件会使用基类的赋值运算符函数将基类组件整个打包复制过去
    若派生类中新增了指针数据变量指向动态分配的内存,则请显式的重新定义赋值函数吧。
    析构函数也是如此。

关于友元函数

由于友元函数并不是类的成员,因此他不能被继承。我一直理解为在派生类中也一定要重新定义相应的友元函数。但是在做练习时候我发现,即使不重新定义友元函数,派生类对象仍旧可以调用基类的友元函数(例如<<重载函数,就会调用基类的友元函数将基类的数据成员显式出来)
若派生类在基类的基础上添加了新的成员数据,那还是要重新定义友元函数了。

Comments

2016-03-07