C++之继承

C++ 继承 语法 程序设计 软件工程


若有理解不当,欢迎各位指正!


作者:吴博文 中山大学数据科学与计算机学院
本人博客:http://blog.csdn.net/bowen_wu_

一、继承基本概念

通过继承,所构建的新的类是基于可靠的基类(base class),所以通过这种机制,可以提高代码的复用率(reuseability),从而减少新类的调试的工作量,从而提高编码的效率。同时,由于存在继承这个机制,有很多的组织专门编写这些基类,进一步提高编码的效率。
值得注意的是,继承的过程中也应该注意派生类与基类之间的逻辑关系。比如,一辆奔驰是一辆车,一辆车是一种交通工具,那么这其中就可以是车继承交通工具、奔驰继承车。

二、基本语法

class 派生类名:继承访问控制 基类类名{
成员访问控制:
成员声明列表;
};
其中一个类可以同时继承多个类,只用用逗号隔开即可。如下:
class 派生类名:继承访问控制 基类类名, 继承访问控制 基类类名, 。。。{
成员访问控制:
成员声明列表;
};

C++所支持的基本继承形式包括以下几种
这里写图片描述
个人认为其中最麻烦的是多重继承,本人还没有搞懂,以后大家一起探讨。
继承中成员的访问方法:

// Created by BowenWu in 20160419
#include <iostream>
using namespace std;
class base_first {
    public:
        int base_1;
        int get() {
            return base_1;
        }
    public:
        base_first() {
            cout << "base_first" << endl;
            base_1 = 10;
        }
};

class derived_first : public base_first{
    public:
        int derived_1;
        int get() {
            return derived_1;
        }
    public:
        derived_first() {
            cout << "derived_first" << endl;
            derived_1 = 9;
        }
};
class derived_second:public derived_first {
    public:
        int derived_1;
        int get() {
            return derived_1;
        }
    public:
        derived_second() {
            cout << "derived_second" << endl;
            derived_1 = 8;
        }
};

int main() {
    derived_second aaa;
    //  try to use int base_1 in class base_first
    // since there is no same name variable in the
    // inheritance relation, so we can use it directly
    cout << aaa.base_1 << endl;
    // try to use int derived_1 in class derived_second
    // easy to see that compiler use derived_1 in
    // class derived_second
    // but how to use derived_1 in class derived_first?
    cout << aaa.derived_1 << endl;
    // in this way
    // but I don't think it is a good practice
    cout << aaa.derived_first::derived_1 << endl;
    cout << aaa.derived_first::base_first::base_1 << endl;
    cout << endl << "Use Function:" << endl;
    // end
    // use function from base class is similar
    cout << aaa.get() << endl;
    cout << aaa.derived_first::get() << endl;
    cout << aaa.derived_first::base_first::get() << endl;

}

这就是基本的访问方式, 但是在C++编程中,我们不应该使用这种方式,因为这种方式将数据成员放在public中,破坏了class的封装性,也失去了面向对象编程的重要价值。
代码中使用的一个操作符”::”称作域作用符,个人理解一个类中称作一个域,而“aaa.derived_first::base_first::base_1”中aaa后面的点,可能表示derived_first看做aaa的一个数据成员。
其实使用函数与访问数据成员等效,可以将函数和数据成员看做同一个系列。

各种继承的访问权限

这里其实很好理解,个人认为可以总结成private < protected < public,当继承方式与基类中的声明方式不同时,只用取相对权限较小的那个就是了。这里权限应该理解成这个数据成员变成了派生类中相应权限的成员一部分,即对于客户端代码来讲的权限,而不是对于这个派生类而言是private、public。
值得注意的是在基类中private的成员无论如何都不能访问。
继承成员的访问权限

protected访问控制

protected是C++继承中特有的一种访问控制,对于客户端代码来讲,他与private相同,但在继承的体系中,他与public类似,也就是说他是介于private和public之间的一种访问控制。

恢复基类的访问权限

基类中的public或protected成员,因使用protected或private继承访问控制而导致在派生类中的访问方式发生改变,可以使用“访问声明”恢复为原来的访问控制方式
访问声明的形式
基类名::成员名;(放于适当的成员访问控制后)
使用情景
在派生类中希望大多数继承成员为protected或private,只有少数希望保持为基类原来的访问控制方式。
对于访问权限的恢复,各个编译器有一些细节的差异,应该注意处理。
例:

// Created by BowenWu in 20160419
#include <iostream>
using namespace std;
class base_first {
    public:
        int base_1;
        int get() {
            return base_1;
        }
    public:
        base_first() {
            cout << "base_first" << endl;
            base_1 = 10;
        }
};

class derived_first : private base_first{
    public:
        // 恢复访问权限
        // 基类名加域作用符加成员
        base_first::base_1;
        int derived_1;
        int get() {
            return derived_1;
        }
    public:
        derived_first() {
            cout << "derived_first" << endl;
            derived_1 = 9;
        }
};

int main() {
    derived_first aaa;
    cout << aaa.base_1++ << endl;
    cout << aaa.base_1 << endl;
}

基类成员方法的重定义

其实就是和定义一个成员方法一样,当定义的成员方法的声明与函数首部与基类的成员方法完全相同时,成为重定义。若函数名相同,但是函数首部不同,称作重载。其实在调用一个成员方法时,编译器是按照一定的规则寻找该调用哪个函数。
这里写图片描述

屏蔽继承成员

目的:
使得客户代码通过派生类对象不能访问继承成员。
方法:
–使用继承访问控制protected和private(真正屏蔽)
–在派生类中成员访问控制protected或private之后定义与继承成员函数相同的函数原型,而函数体为空(非真正屏蔽,仍可通过使用“基类名::成员名”访问)

继承成员重命名

目的:
解决名字冲突。
在派生类中选择更合适的术语命名继承成员。
方法:
–在派生类中定义新的函数,该函数调用旧函数;屏蔽旧函数。
–在派生类中定义新的函数,该函数的函数体与旧函数相同。

类型兼容性

赋值运算符的兼容性

可以将后代类的对象赋值给祖先类对象,反之不可。
每个派生类对象包含一个基类部分,这意味着可以将派生类对象当作基类对象使用。

指针与引用的兼容性

指向基类对象的指针也可指向公有派生类对象,要特别注意是公有派生!!

强制类型转换

派生类可以强制转换为基类,而基类不能强制转换为派生类。

命名冲突

当派生类继承的两个基类中有相同名字的数据成员或成员方法时,会发生这种情况。可以通过重定义或者域作用符来解决这个问题。详见下面的样例代码:

class BASE1 {
public: void show() { cout << i << "\n"; }
protected:  int i;
};
class BASE2 {
public: void show() {   cout << j << "\n"; }
protected:  int j;
};
// 多重继承引起名字冲突:DERIVED的两个基类BASE1和//BASE2有相同的名字show
class DERIVED: public BASE1, public BASE2 {
public: 
    void set(int x, int y)  {  i = x;  j = y;   }
};// 派生类在编译时不出错:C++语法不禁止名字冲突。
int main()
{   
           DERIVED obj;      // 声明一个派生类的对象
    obj.set(5, 7);          // set()是DERIVED类自身定义的

           // obj.show();
    // 二义性错误,编译程序无法决定调用哪一个版本

           obj.BASE1::show();   
    // 正确,显式地调用从BASE1继承下来show()

           obj.BASE2::show();       
    // 正确,显式地调用从BASE2继承下来show()
                      :
}                                                               

三、重复继承时的数据成员

这里写图片描述
对于这种情况,需要引进虚继承的概念

// Created by BowenWu in 20160419
#include <iostream>
using namespace std;
class base_first {
    public:
        int base_1;
        int get() {
            return base_1;
        }
    public:
        base_first() {
            cout << "base_first" << endl;
            base_1 = 10;
        }
};

class derived_first : public base_first{
    public:
        int derived_1;
        int get() {
            return derived_1;
        }
    public:
        derived_first() {
            cout << "derived_first" << endl;
            derived_1 = 9;
        }
};
class derived_second:public base_first {
    public:
        int derived_1;
        int get() {
            return derived_1;
        }
    public:
        derived_second() {
            cout << "derived_second" << endl;
            derived_1 = 8;
        }
};
class derived_third:public derived_first, public derived_second {};

int main() {
    derived_third aaa;
    // first let's try to use the variable that derived_first
    // and derived_second both have.
    cout << aaa.derived_1 << endl; //compile error
    // since this is ambiguous

}

对于上面这段代码,有二义性的错误。
但我们可以通过下面这种方式来分别访问derived_first和derived_second 中的base_first


int main() {
    derived_third aaa;
    // derived_first可以理解成aaa中的一个数据成员,故使用.访问
    // 由于继承关系的存在,base_1可以看做derived_first的一个数据成员
    cout << aaa.derived_first::base_1 << endl;
    cout << aaa.derived_second::base_1 << endl;
    aaa.derived_first::base_1 = 1;
    aaa.derived_second::base_1 = 2;
    cout << aaa.derived_first::base_1 << endl;
    cout << aaa.derived_second::base_1 << endl;

}

这段程序的运行结果是:
这里写图片描述
从这段程序我们可以看出derived_third中保存了两个base_first的副本。

四、虚继承

Virtual inheritance is a technique used in C++, where a particular base class in an inheritance hierarchy is declared to share its member data instances with any other inclusions of that same base in further derived classes.
From Wikipedia
简单来说,虚继承就是当一个类同时继承同一个基类两次时,只保存一份数据成员,以避免Diamond Problem
这里写图片描述

// Created by BowenWu in 20160419
#include <iostream>
using namespace std;
class base_first {
    public:
        int base_1;
        int get() {
            return base_1;
        }
    public:
        base_first() {
            cout << "base_first" << endl;
            base_1 = 10;
        }
};
// keyword virtual
class derived_first : virtual public base_first{
    public:
        int derived_1;
        int get() {
            return derived_1;
        }
    public:
        derived_first() {
            cout << "derived_first" << endl;
            derived_1 = 9;
        }
};
class derived_second: virtual public base_first {
    public:
        int derived_1;
        int get() {
            return derived_1;
        }
    public:
        derived_second() {
            cout << "derived_second" << endl;
            derived_1 = 8;
        }
};
class derived_third:public derived_first, public derived_second {};

int main() {
    derived_third aaa;
    // base_1 在aaa中只有一份,可以直接访问
    cout << aaa.base_1;
}

在重复继承没有发生时,虚继承并不会有什么特别的。

发布了24 篇原创文章 · 获赞 9 · 访问量 2万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章