找回密码
 立即注册
收起左侧

C++老鸟日记040 再谈继承

0
回复
6223
查看
[复制链接]
累计签到:41 天
连续签到:1 天
来源: 原创 2018-10-13 22:52:50 显示全部楼层 |阅读模式

马上注册,查看详细内容!注册请先查看:注册须知

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 baizy77 于 2018-10-14 21:56 编辑

版权声明
---------------------------------------------------------------------------------------------------------------------
该文章原创于Qter开源社区(www.qter.org
作者: 女儿叫老白 (白振勇)
转载请注明出处!
---------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
引言:
----------------------------------------------------------------------------
     在实际工作中,您是否经常通过拷贝粘贴代码的方式进行编程,甚至有的时候直接将整个工程拷贝一份,改名,用作其他用途?如果是这样,建议您改变做法,采用继承的方式也许一样能达到目的。下面,我们转入正题,今天我们再来谈谈继承。

正文:
----------------------------------------------------------------------------
       我们来看几个小问题:
问题1) 没有对类的所有成员对象和基类对象的构造函数调用之前,可否进入构造函数体?
       我们先来看一段代码:
construct.h
  1. class CMyObject {
  2. public:
  3.     CMyObject(string name):m_name (name){}
  4. private:
  5.     int m_name;
  6. };

  7. class CPerson {
  8. };

  9. class CUser : public CPerson {
  10. public:
  11.     CUser();
  12. private:
  13.     int m_id;
  14.     CMyObject m_obj;
  15. };

  16. construct.cpp
  17. ----------------------------------------------------------------------------
  18. CUser::CUser():m_id(0), m_obj(“peter”){
  19. }
  20. ----------------------------------------------------------------------------
复制代码

从上述代码使用了类的构造函数初始化表达式,对于CUser来说在调用成员对象和基类对象的的构造函数之前,就没有办法进入CUser的构造函数体,这是C++机制能够保证的。即使没有编写构造函数,编译器产生默认的构造函数,执行过程也是如此(先进入类的成员变量的构造函数,后进入类的构造函数)。

问题2) 如何调用基类的接口?
       这个问题比较简单,我们来看如下代码:

  1. header.h
  2. ----------------------------------------------------------------------------
  3. class CBase {
  4. public:
  5.     virtual bool onEvent(CEvent* pEvt){
  6.     ……
  7.     return true;
  8.     }
  9. };

  10. class CDerived : public CBase {
  11. public:
  12.     virtual bool onEvent(CEvent* pEvt){
  13.     ……
  14.     return true;
  15.     }
  16. };

  17. class  CChild : public CDerived {
  18. public:
复制代码

       在子类中,如果需要调用父类的接口可以直接用父类::函数名的语法。即使该函数在父类中未提供是在父类的父类中提供的,也是按照此种语法编写代码,就像上述代码一样。onEvent()接口是在CBase类提供,在CChild类中调用时仍然使用CDerived:: onEvent()的写法,而不是CBase:: onEvent()。

问题3) 构造函数和析构函数可以被继承吗
       构造函数和析构函数是不能被继承的,因为他们只知道在特定的类层次上所执行的操作,所以该类以下所有层次的构造函数和析构函数都应该被调用。
       operator=也不能被继承。它完成类似构造函数的操作。继承后,他的含义未必是有右边的对象初始化左边的对象。
问题4) 类的静态成员函数可以被继承吗?
       类的静态成员函数与非静态成员函数的共同点(注1):
       1)  他们均可被集成到派生类中
       2)  如果我们重新定义一个静态成员函数,所有在基类中的其他重载函数均会被隐藏。
       静态成员函数不可以是虚函数。
问题5)  使用private继承的目的是什么(注2)?
       似乎,使用组合的方式用一个private对象进行组合也可以达到private继承的目的。但是,当我们希望某一个类的行为看起来不像基类时(我们在类的内部却希望使用基类的接口),使用private继承是很好的选择。
问题6)  如何对私有继承成员公有化(注3)?
       只要使用public关键字声明这些接口即可。
privatederived.cpp
  1. ----------------------------------------------------------------------------
  2. class base {
  3. public:
  4.        int func1();
  5.        int func2();
  6. };

  7. class CDerived : privateCBase {
  8. public:
  9.        using CBase::func1;
  10.        using CBase::func2;
  11. };

  12. void myfunction() {
  13.        CDerived derivedObject;
  14.        derivedObject.func1();
  15.        derivedObject.func2();
  16. }
  17. ----------------------------------------------------------------------------
复制代码

问题7)  哪些运算符可以自动继承到派生类?哪些不行?
       除了赋值运算符之外,其余运算符均可以集成到派生类中。(注4)
问题8) 派生类的拷贝构造函数中是否需要调用基类的拷贝构造函数?
派生类的拷贝构造函数必须调用基类的拷贝构造函数,否则就会因为编译器强制调用基类的默认构造函数而出现意想不到的结果。
copyconstruction.cpp
  1. ----------------------------------------------------------------------------
  2. class CMyObject;
  3. class CBase{
  4. public:
  5.        CBase(const CBase& right);
  6. };

  7. class CDerived : publicCBase {
  8. public:
  9.       CDerived(constDerived& right): CBase(right), m_obj(right.m_obj){
  10.        ……
  11. }
  12. private:
  13.        CMyObject m_obj;
  14. }
  15. ----------------------------------------------------------------------------
复制代码

       在上述代码中可以看出,CDerived派生类的拷贝构造函数中,必须先调用基类的拷贝构造函数。这里还遵循了另外一个原则:构造函数中所有成员变量的构造函数都必须在初始化表达式中完成调用(不管是显示的还是隐式的)。

结语:
----------------------------------------------------------------------------
       本文中讨论了关于继承的几个小问题,澄清了一些疑问,这些问题对于我们日常编程中有些是常用的知识,而有些比较偏僻,算是对我们的C++知识的一些补充。

参考资料
----------------------------------------------------------------------------
注1:《C++编程思想》两卷合订本中文版(P349),(美) Bruce Eckel  Chuck Allison著
注2:《C++编程思想》两卷合订本中文版(P352),(美) Bruce Eckel  Chuck Allison著
注3:《C++编程思想》两卷合订本中文版(P352),(美) Bruce Eckel  Chuck Allison著
注4:《C++编程思想》两卷合订本中文版(P353),(美) Bruce Eckel  Chuck Allison著
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了