详解类与对象——继承

(^ _ ^)

一.基本语法

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

class A : public B;

A 类称为子类 或 派生类

B 类称为父类 或 基类

派生类中的成员,包含两大部分

一类是从基类继承过来的,一类是自己增加的成员。

从基类继承过过来的表现其共性,而新增的成员体现了其个性。

//公共页面

class BasePage

{

public:

    void header()

    {

        cout << "首页、公开课、登录、注册...(公共头部)" << endl;

    }

  

    void footer()

    {

        cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;

    }

    void left()

    {

        cout << "Java,Python,C++...(公共分类列表)" << endl;

    }

  

};

  

//Java页面

class Java : public BasePage

{

public:

    void content()

    {

        cout << "JAVA学科视频" << endl;

    }

};

//Python页面

class Python : public BasePage

{

public:

    void content()

    {

        cout << "Python学科视频" << endl;

    }

};

//C++页面

class CPP : public BasePage

{

public:

    void content()

    {

        cout << "C++学科视频" << endl;

    }

};

  

void test01()

{

    //Java页面

    cout << "Java下载视频页面如下: " << endl;

    Java ja;

    ja.header();

    ja.footer();

    ja.left();

    ja.content();

    cout << "--------------------" << endl;

  

    //Python页面

    cout << "Python下载视频页面如下: " << endl;

    Python py;

    py.header();

    py.footer();

    py.left();

    py.content();

    cout << "--------------------" << endl;

  

    //C++页面

    cout << "C++下载视频页面如下: " << endl;

    CPP cp;

    cp.header();

    cp.footer();

    cp.left();

    cp.content();

  
  

}

  

int main() {

  

    test01();

  

    system("pause");

  

    return 0;

}

二.继承方式

继承方式一共有三种:

  • 公共继承

  • 保护继承

  • 私有继承

(类似权限)
代码示例:

class Base1

{

public:

    int m_A;

protected:

    int m_B;

private:

    int m_C;

};

  

//公共继承

class Son1 :public Base1

{

public:

    void func()

    {

        m_A; //可访问 public权限

        m_B; //可访问 protected权限

        //m_C; //不可访问

    }

};

  

void myClass()

{

    Son1 s1;

    s1.m_A; //其他类只能访问到公共权限

}

  

//保护继承

class Base2

{

public:

    int m_A;

protected:

    int m_B;

private:

    int m_C;

};

class Son2:protected Base2

{

public:

    void func()

    {

        m_A; //可访问 protected权限,父类中公共成员到子类变成保护权限

        m_B; //可访问 protected权限

        //m_C; //不可访问

    }

};

void myClass2()

{

    Son2 s;

    //s.m_A; //不可访问

}

  

//私有继承

class Base3

{

public:

    int m_A;

protected:

    int m_B;

private:

    int m_C;

};

class Son3:private Base3

{

public:

    void func()

    {

        m_A; //可访问 private权限,变成私有权限

        m_B; //可访问 private权限,变成私有权限 

        //m_C; //不可访问

    }

};

class GrandSon3 :public Son3

{

public:

    void func()

    {

        //Son3是私有继承,所以继承Son3的属性在GrandSon3中都无法访问到

        //m_A;

        //m_B;

        //m_C;

    }

};

三.继承中的对象模型

在父类中所有非静态的成员属性都会被子类继承下去
父类中的私有权限确实会被子类继承,但是会被编译器隐藏

class Base
{

public:

    int m_A;

protected:

    int m_B;

private:

    int m_C; //私有成员只是被隐藏了,但是还是会继承下去

};

  

//公共继承

class Son :public Base

{

public:

    int m_D;

};

  

void test01()

{

    cout << "sizeof Son = " << sizeof(Son) << endl;

}

  

int main() {

  

    test01();

  

    system("pause");

  

    return 0;

}

//结果是16

四.继承中构造和析构顺序

父类构造——子类构造——子类析构——父类析构

(白发人送黑发人(大雾…
(两面包夹芝士

class Base

{

public:

    Base()

    {

        cout << "Base构造函数!" << endl;

    }

    ~Base()

    {

        cout << "Base析构函数!" << endl;

    }

};

  

class Son : public Base

{

public:

    Son()

    {

        cout << "Son构造函数!" << endl;

    }

    ~Son()

    {

        cout << "Son析构函数!" << endl;

    }

  

};

  
  

void test01()

{

    //继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

    Son s;

}

  

int main() {

  

    test01();

  

    system("pause");

  

    return 0;

}

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

  • 访问子类同名成员 直接访问即可

  • 访问父类同名成员 需要加作用域

示例:


class Base {

public:

    Base()

    {

        m_A = 100;

    }

  

    void func()

    {

        cout << "Base - func()调用" << endl;

    }

  

    void func(int a)

    {

        cout << "Base - func(int a)调用" << endl;

    }

  

public:

    int m_A;

};

  
  

class Son : public Base {

public:

    Son()

    {

        m_A = 200;

    }

  

    //当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数

    //如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域

    void func()

    {

        cout << "Son - func()调用" << endl;

    }

public:

    int m_A;

};

  

void test01()

{

    Son s;

  

    cout << "Son下的m_A = " << s.m_A << endl;

    cout << "Base下的m_A = " << s.Base::m_A << endl;

  

    s.func();

    s.Base::func();

    s.Base::func(10);

  

}

int main() {

  

    test01();

  

    system("pause");

    return 0;

}

总结:

  1. 子类对象可以直接访问到子类中同名成员

  2. 子类对象加作用域可以访问到父类同名成员

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

六.继承中的同名静态成员

静态成员和非静态成员出现同名,处理方式一致

  • 访问子类同名成员 直接访问即可

  • 访问父类同名成员 需要加作用域

示例:


class Base {

public:

    static void func()

    {

        cout << "Base - static void func()" << endl;

    }

    static void func(int a)

    {

        cout << "Base - static void func(int a)" << endl;

    }

  

    static int m_A;

};

  

int Base::m_A = 100;

  

class Son : public Base {

public:

    static void func()

    {

        cout << "Son - static void func()" << endl;

    }

    static int m_A;

};

  

int Son::m_A = 200;

  

//同名成员属性

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()

{

    //通过对象访问

    cout << "通过对象访问: " << endl;

    Son s;

    s.func();

    s.Base::func();

  

    cout << "通过类名访问: " << endl;

    Son::func();

    Son::Base::func();

    //出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问

    Son::Base::func(100);

}

int main() {
    //test01();

    test02();

  

    system("pause");

  

    return 0;

}

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

七.多继承语法

C++允许一个类继承多个类

语法: class 子类 :继承方式 父类1 , 继承方式 父类2...
注意:可能会出现不同父类中同名成员出现,需要通过作用域加以区分
(因此实际开发中不建议使用多继承)

```C++

class Base1 {

public:

    Base1()

    {

        m_A = 100;

    }

public:

    int m_A;

};

  

class Base2 {

public:

    Base2()

    {

        m_A = 200;  //开始是m_B 不会出问题,但是改为mA就会出现不明确

    }

public:

    int m_A;

};

  

//语法:class 子类:继承方式 父类1 ,继承方式 父类2

class Son : public Base2, public Base1

{

public:

    Son()

    {

        m_C = 300;

        m_D = 400;

    }

public:

    int m_C;

    int m_D;

};

  
  

//多继承容易产生成员同名的情况

//通过使用类名作用域可以区分调用哪一个基类的成员

void test01()

{

    Son s;

    cout << "sizeof Son = " << sizeof(s) << endl;

    cout << s.Base1::m_A << endl;

    cout << s.Base2::m_A << endl;

}

  

int main() {

  

    test01();

  

    system("pause");

  

    return 0;

}

八.菱形继承

菱形继承概念:

​ 两个派生类继承同一个基类

​ 又有某个类同时继承者两个派生类

​ 这种继承被称为菱形继承,或者钻石继承

菱形继承中也存在数据的二义性,这里的二义性是由于他们间接都有相同的基类导致的。 这种菱形继承除了带来二义性之外,还会有有数据冗余浪费内存空间。

详解类与对象——继承_第1张图片

C++中引入了虚基类,

其作用是 在间接继承共同基类时只保留一份基类成员

class Animal

{

public:

    int m_Age;

};

  

//继承前加virtual关键字后,变为虚继承

//此时公共的父类Animal称为虚基类

class Sheep : virtual public Animal {};

class Tuo   : virtual public Animal {};

class SheepTuo : public Sheep, public Tuo {};

  

void test01()

{

    SheepTuo st;

    st.Sheep::m_Age = 100;

    st.Tuo::m_Age = 200;

  

    cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;

    cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;

    cout << "st.m_Age = " << st.m_Age << endl;//数据只有一份不会出现不明确的问题

}
int main() {

  

    test01();

  

    system("pause");

  

    return 0;

}

所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度上就有问题并且菱形继承虚继承也带来了性能上的损耗(因为多开了地址来存放偏移量)

总结:

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

  • 利用虚继承可以解决菱形继承问题

  • 不建议使用

你可能感兴趣的:(c++)