「地表最强」C++核心编程(四)类和对象--继承-创新互联

环境:
编译器:CLion2021.3;操作系统:macOS Ventura 13.0.1

创新互联公司长期为上1000+客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为城北企业提供专业的网站设计、网站制作,城北网站改版等技术服务。拥有10余年丰富建站经验和众多成功案例,为您定制开发。文章目录
  • 一、继承的基本语法
  • 二、继承方式
    • 2.1 public继承
    • 2.2 protected继承
    • 2.3 private继承
    • 2.4 继承规则
  • 三、继承中的对象模型
  • 四、继承中的构造和析构顺序
  • 五、继承同名成员处理方式
  • 六、继承同名静态成员处理方式
  • 七、多继承语法
  • 八、菱形继承和虚继承

地表最强C++系列传送门:
「地表最强」C++核心编程(一)内存分区模型
「地表最强」C++核心编程(二)引用
「地表最强」C++核心编程(三)函数提高
「地表最强」C++核心编程(四)类和对象----封装
「地表最强」C++核心编程(五)文件操作——暂未更新

继承是C++的三大基本特性之一。

一、继承的基本语法

语法: class 子类 : 继承方式 父类

其中子类也称为派生类,父类也称为基类。派生类中的成员,包含两大部分:一类是从基类继承过来的,一类是自己增加的成员。从基类继承过过来的表现其共性,而新增的成员体现了其个性。

//公共页面
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;
    }
};
二、继承方式

继承方式一共有三种:公共继承、保护继承、私有继承。

2.1 public继承
class Base1 {public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};

//公共继承
class Son1 : public Base1 {public:
    void func() {m_A = 10; //可访问 public权限
        m_B = 10; //可访问 protected权限
//        m_C; //不可访问
    }
};

void myClass() {Son1 s1;
    s1.m_A; //ok,其他类只能访问到公共权限
//    s1.m_B; //err,其他类只能访问到公共权限,m_B是protected,类外不可以访问
}
2.2 protected继承
//保护继承
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; //不可访问,此时m_A变成了protected
}
2.3 private继承
//私有继承
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;//err
//        m_B;//err
//        m_C;//err
    }
};
2.4 继承规则

在这里插入图片描述

三、继承中的对象模型

父类中所有非静态成员属性都会被继承下去,私有成员属性只是访问不到,因为被编译器隐藏了,但是也被继承下去了。

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;//16,说明private也被继承了
}
四、继承中的构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数,此时先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反。

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;
}
五、继承同名成员处理方式

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

  • 访问子类同名成员,直接访问即可,也就是对象.属性
  • 访问父类同名成员,需要加作用域,也就是对象.父类::属性

需要注意的一点是:当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数,否则无法访问。强调一下,只要函数名相同,其他的不管一不一样,都会被隐藏。

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;//加个作用域就可以访问父类的

    s.func();
    s.Base::func();
    //子类中的同名成员函数会隐藏掉父类有所有同名成员函数,包括重载的,要想访问父类中被隐藏的同名成员函数的需要加作用域
//    s.func(10);//err,父类中的有参函数被隐藏,而子类又没有有参函数,需要加作用域
    s.Base::func(10);//ok

}
六、继承同名静态成员处理方式

静态成员和非静态成员出现同名,处理方式一致,只不过有两种访问的方式:通过对象和通过类名。比非静态多了一个通过类名访问,这是静态成员本身的特性所导致的。
关于静态成员,可以参考「地表最强」C++核心编程(四)类和对象—对象初始化和清理第八点。

class Base {public:
    static int m_A;

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

    static void func(int a) {cout<< "Base - static void func(int a)"<< endl;
    }
};
int Base::m_A = 100;

class Son : public Base {public:
    static int m_A;

    static void func() {cout<< "Son - static void func()"<< endl;
    }
};
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;//这样通过子类
//    cout<< "Base 下 m_A = "<< 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);
//    Son::func(100);//err,父类中的同名函数被隐藏了
}
七、多继承语法

C++允许一个类继承多个类,就是多继承。但多继承可能会引发从父类中继承多个同名成员,需要加作用域区分。C++实际开发中不建议用多继承。

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

class Base1 {public:
    int m_A;

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

class Base2 {public:
    int m_A;

public:
    Base2() {m_A = 200;  //若是m_B则不会出问题,但是改为mA就会出现不明确
    }
};

//语法:class 子类:继承方式 父类1 ,继承方式 父类2
class Son : public Base2, public Base1 {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<< endl;
    cout<< s.Base2::m_A<< endl;
}
八、菱形继承和虚继承

两个派生类继承同一个基类,又有某个类同时继承这两个派生类,这种继承被称为菱形继承,或者钻石继承。
菱形继承的问题:
1.羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
2.草泥马继承自动物的数据继承了两份,但这个据我们只需要一份,这导致资源浪费而且毫无意义。
通过虚继承就可以解决菱形继承带来的问题。

语法: class 子类 : virtual 继承方式 父类

virtual关键字使得继承方式变成了虚继承,此时两个子类从同一父类那里继承来的是虚基类指针vbptr(virtual base pointer),这个指针指向了各自的vbtable(虚基类表),这个表中记录了一个偏移量,通过这个偏移量就可以找到需要的数据,这个数据只有一份。
在这里插入图片描述

class Animal {public:
    int m_Age;
};

//虚继承,此时公共的父类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;
    cout<< "st.Sheep::m_Age = "<< st.Sheep::m_Age<< endl;//100
    
    st.Tuo::m_Age = 200;//由于虚继承数据只有一份,操作这个会更改之前的赋值

    cout<< "st.Sheep::m_Age = "<< st.Sheep::m_Age<< endl;//200,确实更改了
    cout<< "st.Tuo::m_Age = "<< st.Tuo::m_Age<< endl;//200
    cout<< "st.m_Age = "<< st.m_Age<< endl;//由于virtual,这种访问方式可以了,否则是错误的
}

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享标题:「地表最强」C++核心编程(四)类和对象--继承-创新互联
网页路径:http://pwwzsj.com/article/dcodeh.html