C++学习(四)_继承

1.继承的三大分类

继承分为三类:

  1. 公共继承

  2. 保护继承

  3. 私有继承

他们的特点如下图所示:


2.继承基本语法

继承的好处: 减少重复代码

语法:

class 子类 : 继承方式 父类

子类 也称 派生类

父类 也称 基类

例1:不使用继承
#include <iostream>
using namespace std;

//Java页面
class Java {
public:
    void header(){
        cout << "header" << endl;
    }

    void footer(){
        cout << "footer" << endl;
    }

    void left(){
        cout << "left" << endl;
    }

    void content(){
        cout << "***Java***" << endl;
    }
};

class Python {
public:
    void header(){
        cout << "header" << endl;
    }

    void footer(){
        cout << "footer" << endl;
    }

    void left(){
        cout << "left" << endl;
    }

    void content(){
        cout << "***Python***" << endl;
    }
};

class Cpp{
public:
    void header(){
        cout << "header" << endl;
    }

    void footer(){
        cout << "footer" << endl;
    }

    void left(){
        cout << "left" << endl;
    }

    void content(){
        cout << "***C++***" << endl;
    }
};

int main()
{
    Java java;
    java.header();
    java.footer();
    java.left();
    java.content();

    Python python;
    python.header();
    python.footer();
    python.left();
    python.content();

    Cpp cpp;
    cpp.header();
    cpp.footer();
    cpp.left();
    cpp.content();

    return 0;
}

我们发现,上述代码有很多重复的部分,十分冗余。

例2:使用继承
#include <iostream>
using namespace std;

class BasePage{
public:
    void header(){
        cout << "header" << endl;
    }

    void footer(){
        cout << "footer" << endl;
    }

    void left(){
        cout << "left" << endl;
    }

    void content(){
        cout << "***basepage***" << endl;
    }
};

//Java页面
class Java : public BasePage{
public:
    void content(){
        cout << "***Java***" << endl;
    }
};

class Python : public BasePage{
public:
    void content(){
        cout << "***Python***" << endl;
    }
};

class Cpp : public BasePage{
public:
       void content(){
        cout << "***C++***" << endl;
    }
};

int main()
{
    Java java;
    java.header();
    java.footer();
    java.left();
    java.content();

    Python python;
    python.header();
    python.footer();
    python.left();
    python.content();

    Cpp cpp;
    cpp.header();
    cpp.footer();
    cpp.left();
    cpp.content();

    return 0;
}

从上面两个例子不难看出,使用继承后代码看起来更加简洁易懂,省去了很多重复代码.


3.继承中的对象模型

  1. 父类中所有非静态成员属性都会被子类继承下去

  2. 父类中私有成员属性是被编译器給隐藏了,因此是访问不到,但是确实被继承下去了

    补充:利用开发人员命令提示工具查看对象模型


4.继承中的构造和析构顺序

例3:
#include <iostream>
using namespace std;

class Base{
public:
    Base(){
        cout << "Base的构造函数" << endl;
    }

    ~Base(){
        cout << "Base的析构函数" << endl;
    }
};

class Son : public Base{
public:
    Son(){
        cout << "Son的构造函数" << endl;
    }

    ~Son(){
        cout << "Son的析构函数" << endl;
    }
};

int main()
{
    Son s;
}

运行结果:

由运行结果我们可知:先构造父类,在构造子类,析构的顺序和构造的顺序相反


5.继承同名成员处理方式

当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
例4:
#include <iostream>
using namespace std;

class Base{
public:
    int m_A;
public:
    Base(){
        m_A = 100;
    }
    void func(){
        cout << "Base * func()调用" << endl;
    }
    void func(int a){
        cout << "Base * func(int a)调用" << endl;
    }
};

class Son : public Base{
public:
    int m_A;
public:
    Son(){
        m_A = 200;
    }

    void func(){
        cout << "Son  * func()调用" << endl;
    }
};

//同名成员属性
void test01(){
    Son s;
    cout << "Son  下的m_A = " << s.m_A << endl;
    cout << "Base 下的m_A = " << s.Base::m_A << endl;
}

//同名成员函数
void test02(){
    Son s;
    s.func();
    s.Base::func();
    s.Base::func(100);
}

int main()
{
    test01();
    cout << "*****************" << endl;
    test02();
}

运行结果:

总结:

  1. 子类对象可以直接访问到子类中同名成员
  2. 子类对象加作用域可以访问到父类同名成员
  3. 当子类与父类拥有同名的成员函数,子类会隐藏父类中的同名成员函数,加作用域可以访问到父类中同名函数

### 6.继承中的同名静态成员处理

同名静态成员处理和非静态处理方式一样,只不过有两种访问的方式(通过对象和通过类名)

具体方法如下:

例5:
#include <iostream>
using namespace std;

class Base{
public:
    static int m_A;
public:
    Base(){
        m_A = 100;
    }
    static void func(){
        cout << "Base * func()调用" << endl;
    }
    static void func(int a){
        cout << "Base * func(int a)调用" << endl;
    }
};

class Son : public Base{
public:
    static int m_A;
public:
    Son(){
        m_A = 200;
    }

    static void func(){
        cout << "Son  * func()调用" << endl;
    }
};

//同名静态成员属性
void test01(){
    //通过对象访问
    cout << "通过对象访问" << endl;
    Son s;
    cout << "Son  下的m_A = " << s.m_A << endl;
    cout << "Base 下的m_A = " << s.Base::m_A << endl;

    //通过类名访问
    cout << "通过类名访问" << endl;
    cout << "Son  下的m_A = " << Son::m_A << endl;
    //第一个::代表通过类名方式访问  第二个::代表访问父类作用域下
    cout << "Base 下的m_A = " << Son::Base::m_A << endl;
}

//同名静态成员函数
void test02(){
    Son s;

    cout << "通过对象访问" << endl;
    s.func();
    s.Base::func();

    cout << "通过类名访问" << endl;
    Son::func();
    Son::Base::func();
    Son::Base::func(100);
}

int Base::m_A = 100;
int Son::m_A = 200;

int main()
{
    test01();
    cout << "*****************" << endl;
    test02();
}

运行结果:


### 7.多继承语法

语法:

class 子类: 继承方式 父类1,继承方式 父类2...
例6:
#include <iostream>
using namespace std;

class Base1{
public:
    Base1(){
        m_A = 100;
    }
public:
    int m_A;
};

class Base2{
public:
    int m_B;
public:
    Base2(){
        m_B = 200;
    }
};

//子类,需继承Base1和Base2
class Son : public Base1, public Base2{
public:
    int m_C;
    int m_D;
public:
    Son(){
        m_C = 300;
        m_D = 400;
    }
};

void test01(){
    Son s;
    cout << "Sizeof(son) = " << sizeof(s) << endl;
    //注:当继承的父类中含有同名成员,需要加作用域区分
    //cout << s.Base1::m_A
    //cout << s.Base2::m_B
}


int main()
{
    test01();
}

### 8.菱形继承

即:有两个子类B, C继承一个父类A,

又有一个子类D继承B和C

#include <iostream>
using namespace std;

//动物类
class Animal{
public:
    int m_Age;
};

//利用虚继承,可以解决
//继承之前 加上关键字 virtual 变为虚继承
//Animal类称为 虚基类

//羊类
class Sheep : virtual public Animal{};

//驼类
class Camel : virtual public Animal{};

//羊驼
class SheepTuo : public Sheep, public Camel{};

void test01(){
    SheepTuo st;
    st.Sheep::m_Age = 18;
    st.Camel::m_Age = 28;
    //当菱形继承,两个父类有相同的数据,需要加以作用域区分
    cout << "Sheep::m_Age = " << st.Sheep::m_Age << endl;
    cout << "Camel::m_Age = " << st.Camel::m_Age << endl;
    cout << "m_Age = " << st.m_Age << endl;
    //这份数据我们知道 只需有一份就可以,菱形继承导致数据有两份,资源浪费
}

int main()
{
    test01();
}

总结:

  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费且毫无意义
  • 利用虚继承可以解决菱形继承问题

虚继承:子类继承的是两个指针,两个指针会通过偏移量在虚基列表中找到唯一的数据,这个数据只有一个

以上为我总结的C++继承方面的主要知识.
如有不对,敬请指正

posted @ 2019-10-09 21:56  美团骑手阿霖  阅读(181)  评论(2编辑  收藏  举报