C++面向对象(类和对象)—— 对象特性(继承、封装、多态)

微软技术分享 微软全球最有价值专家
全栈领域优质创作者
博客专家认证
2025-06-07 08:03:38

文章目录

六、继承

  • 继承面向对象的三大特性之一
  • 优点:减少大量而重复的代码
  • 语法:class 子类(派生类) : 继承方式 父类(基类)
  • 派生类中的成员包含两大部分: 1.一类是从基类继承过来的,另一类是自己增添的成员 2.从基类继承过来的特性表现其共性,而新增的成员体现了其个性

https://i-blog.csdnimg.cn/direct/3bd58adcb87b4b07a804dbefac44d605.png


6.1继承的基本语法

  • 例如在我们看到的很多网站中,都有公共的头部,公共的底部,或者公共的左右侧列表,只有中间内容各显神态
  • 接下来我们分别要利用普通写法继承的方式来实现网页中的内容,来对比一下继承存在的意义,以及其所具有的便利性

https://i-blog.csdnimg.cn/direct/79ad143aa433432a8ff1f74e68976cd9.png


  • 写一个普通实现的 Java教学页面
#include<iostream>
using namespace std;

class Java {
public:
    void header(){
        cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
    }

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

    void left(){
        cout << "Java、C++、C、Python...(公共分类列表)" << endl;
    }

    void content() {
        cout << "Java 学科视频(页面内容)" << endl;
    }
};

void test1()
{
    cout << "Java 下载视频页面如下:" << endl;
    Java ja;
    ja.header();
    ja.footer();
    ja.left();
    ja.content();
}
int main(){test1();return 0;}

https://i-blog.csdnimg.cn/direct/c3b432419fe5438c82b6e12c7b7bcab3.png


  • Python、C++ 教学页面
#include<iostream>
using namespace std;

class Java {
public:
    void header(){
        cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
    }

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

    void left(){
        cout << "Java、C++、C、Python...(公共分类列表)" << endl;
    }

    void content() {
        cout << "Java 学科视频(页面内容)" << endl;
    }
};

class Python {
public:
    void header() {
        cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
    }

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

    void left() {
        cout << "Java、C++、C、Python...(公共分类列表)" << endl;
    }

    void content() {
        cout << "Python 学科视频(页面内容)" << endl;
    }
};

class Cpp {
public:
    void header() {
        cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
    }

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

    void left() {
        cout << "Java、C++、C、Python...(公共分类列表)" << endl;
    }

    void content() {
        cout << "Python 学科视频(页面内容)" << endl;
    }
};

void test1()
{
    cout << "Java 下载视频页面如下:" << endl;
    Java ja;
    ja.header();
    ja.footer();
    ja.left();
    ja.content();
    cout << "-----------------------------------" << endl;
}
void test2() {
    cout << "Python 下载视频页面如下:" << endl;
    Python py;
    py.header();
    py.footer();
    py.left();
    py.content();
    cout << "-----------------------------------" << endl;
}

void test3()
{
    cout << "Cpp 下载视频页面如下:" << endl;
    Cpp cpp;
    cpp.header();
    cpp.footer();
    cpp.left();
    cpp.content();
    cout << "-----------------------------------" << endl;
}
int main(){test1();test2();test3();return 0;}

https://i-blog.csdnimg.cn/direct/8a208464c8914147bd162b64f92e3f6e.png

  • 其实我们可以看到,通过笨拙的方法依然可以得到我们想要的内容,但这样的方法不管是在效率上、空间上…都不尽人意。

  • 继承的高效性 — 继承实现页面
#include<iostream>
using namespace std;

class BasePage//公共部分
{
public:
    void header(){
        cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
    }

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

    void left(){
        cout << "Java、C++、C、Python...(公共分类列表)" << endl;
    }

};

//Java页面:Java 继承(:) BasePage的内容
class Java : public BasePage {
public:
    void content() {
        cout << "Java 学科视频" << endl;
    }
};

//Pthyon页面:Pthyon 继承(:) BasePage
class Python : public BasePage {
public:
    void content() {
        cout << "Pthyon 学科视频" << endl;
    }
};

//Cpp页面:Cpp 继承(:) BasePage
class Cpp : public BasePage {
public:
    void content() {
        cout << "C++ 学科视频" << endl;
    }
};

void test() {
    cout << "Java 下载视频页面如下:" << endl;
    Java ja;
    ja.header();
    ja.footer();
    ja.left();
    ja.content();
    cout << "-----------------------------------" << endl;

    cout << "Python 下载视频页面如下:" << endl;
    Python py;
    py.header();
    py.footer();
    py.left();
    py.content();
    cout << "-----------------------------------" << endl;

    cout << "C++ 下载视频页面如下:" << endl;
    Cpp cpp;
    cpp.header();
    cpp.footer();
    cpp.left();
    cpp.content();
    cout << "-----------------------------------" << endl;
}
int main(){test();return 0;}

https://i-blog.csdnimg.cn/direct/3ba089b236b446c295fc9af4eb0382d9.png


6.2继承方式

  • 继承语法:class 派生类 : 继承方式 基类
  • 继承方式一个有三种:
  • 公共继承
  • 保护继承
  • 私有继承
  • 继承方式图示:

https://i-blog.csdnimg.cn/direct/73c42db35c8049e0b5e60670099b1890.png


  • 公共继承

https://i-blog.csdnimg.cn/direct/d6d4ce1a71294a99ab6e0bd0d5672c34.png

https://i-blog.csdnimg.cn/direct/ef7a0fad73c94e2ebc7cb31f94ccff76.png


  • 保护继承
  • 子类所继承的父类可访问的成员(或行为)变为Protected

https://i-blog.csdnimg.cn/direct/d2e261de0bbe48a286a07a6f0bc30294.png

https://i-blog.csdnimg.cn/direct/9790b59addac46508c6366dfd90a93cf.png


  • 私有继承
  • 子类继承的可访问的父类成员变为Private

https://i-blog.csdnimg.cn/direct/cc0f9b3f60ff418d9ccd9a1281878bd2.png

https://i-blog.csdnimg.cn/direct/074c086d57a84aa9b1a325c58237c935.png


6.3继承中的对象模型

  • 问题:从父类继承而来的成员,哪些属于父类?
#include<iostream>
using namespace std;

class Base {
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;//父类中的私有成员,在子类被隐藏了,但仍可被子类继承
};

//公共继承
class Son1 : public Base {
public:
    int m_D;
};

void test01() {
    cout << "sizeof Son1 == " << sizeof(Son1) << endl;
}
int main(){test01();return 0;}

https://i-blog.csdnimg.cn/direct/9a4f1cb7b4b341fd8cf88254c006f6a4.png

  • 根据代码块的输出,我们可以验证:

  • 父类中私有成员,在子类被隐藏了,但仍可被子类继承

  • 结论:父类中所有非静态成员属性都会被子类继承下去


  • 继承对象模型分布图: VS开发人员命令提示工具查看对象模型
  • **跳转盘符 F: **
  • 跳转文件路径:cd 具体路径
  • 查看命令行:cl / d1 reportSingleClassLayout 类名 “文件名”

https://i-blog.csdnimg.cn/direct/4b2ef7e6ece24d46bd74de445dd21929.png


  • 结论:父类中的私有成员,同样被子类所继承,只是编译器将其隐藏,因此我们访问不到父类中的私有成员

https://i-blog.csdnimg.cn/direct/58a0b266f5b34b9893b2feff885d966b.png


6.4继承中构造与析构顺序

  • 子类继承父类后,当创建子类对象时,也会调用父类中的构造函数
  • 问题:父、子类间的构造和析构顺序先后顺序?
  • 先构造父类,再构造子类****,且在调用子类时,会优先调用子类所属的父类的构造函数,反之调用析构函数
#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;return 0; }

https://i-blog.csdnimg.cn/direct/52ad45719e4348e9b42d18ffe52b1a12.png

  • 图示原理:

https://i-blog.csdnimg.cn/direct/cf3fcb9832204f0bab0dbfd153f66771.png


6.5同名继承的处理方式

  • 问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的对象呢?
  • 访问子类同名成员,直接访问即可
  • 访问父类同名成员,需加入作用域

6.5.1同名成员

  • 同名成员变量(成员属性):
#include<iostream>
using namespace std;

class Base {
public:
    
    Base() {
        m_A = 100;
    }

    int m_A;
};

class Son : public Base {
public:

    Son(){
        m_A = 200;
    }

    int m_A;
};

void test() {
    Son s;
    cout << "Son  下的 s.m_A == " << s.m_A << endl;
    cout << "Base 下的 s.m_A == " << s.Base::m_A << endl;
}
int main(){test();return 0;}

https://i-blog.csdnimg.cn/direct/1961930a9dca4a05a9b5bb14b92fb8bd.png


  • 同名成员函数(成员行为):
class Base {
public:
    
    void func() {
        cout << "Base - func 的调用!" << endl;
    }

    int m_A;
};

class Son : public Base {
public:

    int m_A;
};

//当不存在 Son - func() 时,子类 Son 所实例化出 s,可以直接访问父类 Base 中的公共属性、行为 
void test() { Son s; s.func(); }

  • 当子类与父类具有同名的成员函数 - 通过对象直接调用,子类会优先调用自身的成员函数,因此需要以作用域,加以区分
#include<iostream>
using namespace std;

class Base {
public:
    
    void func() {
        cout << "Base - func 的调用!" << endl;
    }

//函数重载:同一作用域下,函数的名称相同,但ha
    void func(int) {
    cout << "Son - func(int) 的调用!" << endl;
}
    int m_A;
};

class Son : public Base {
public:

    void func() {
        cout << "Son - func 的调用!" << endl;
    }
    int m_A;
};

void test() { 
    Son s; 
    s.func(); 
    s.Base::func();
    s.Base::func(100);//函数重载
}

https://i-blog.csdnimg.cn/direct/1df3eb81f5064045b633766d801bf3f8.png


6.5.2继承同名静态成员处理方式

  • 问题:继承中同名的静态成员在子类对象上如何进行访问?
  • 静态成员与非静态成员同名,处理方式一致
  • 访问子类同名成员,直接访问即可
  • 访问父类同名成员,需加入作用域
  • 静态同名成员属性:
  • 通过对象访问
  • 通过类名访问
#include<iostream>
using namespace std;

class Base {
public:
    static int m_A;//类内声明,类外初始化
};
int Base::m_A = 100;

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

void test() {
    //1.通过对象访问数据
    cout << "通过对象访问:" << endl;
    Son s;
    cout << "Son  - m_A == " << s.m_A << endl;
    cout << "Base - m_A == " << Base::m_A << endl;

    //2.通过类名访问数据
    cout << "通过类名访问数据:" << endl;
    cout << "Son  - m_A == " << Son::m_A << endl;
    //Son::(通过Son类名的访问方式) - Base::m_A(访问父类Base作用域下的m_A)
    cout << "Base - m_A == " << Son::Base::m_A << endl;
}
int main(){test();return 0;}

  • 同名静态成员函数(行为):
  • 通过对象访问
  • 通过类名访问
#include<iostream>
using namespace std;

class Base {
public:

    static void func() {
        cout << "Base - static func()的调用!" << endl;
    }

    static void func(int) {
        cout << "Base - static func(int)的调用!" << endl;
    }
};

class Son : public Base {
public:
    static void func() {
        cout << "Son - static func()的调用!" << endl;
    }
};

void test() {
    //1.通过对象访问
    cout << "通过对象访问:" << endl;
    Son s;
    s.func();
    s.Base::func();

    //2.通过类名访问
    cout << "通过类名访问:" << endl;
    Son::func();
    Son::Base::func();
    Son::Base::func(100);
}
int main(){test();return 0;}

6.6多继承语法

  • C++允许一个类继承多个类(认多个爹)
  • 语法:class 子类 : 继承方式 父类1, 继承方式2 父类2
  • 多继承可能会引发父类中有同名成员出现,需要加作用域区分
    C++实际开发中不建议使用多继承
#include<iostream>
using namespace std;

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

    int m_A;
};

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

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

    int m_C;
    int m_D;
};

void test(){
    Son s;
    cout << "sizeof(s) == " << sizeof(s) << endl;
}
int main(){test();return 0;}

https://i-blog.csdnimg.cn/direct/0cc770308bb9405cad17a6a705b7aa04.png

  • 总结:多继承中如果父类中出现了同名情况,子类调用时要加入作用域做区分

6.7菱形继承

  • 菱形继承的概念: 1.两个派生类继承他一个基类 2.又有某个类同时继承两个派苏类 3.这种继承就被称为菱形继承,或钻石继承

https://i-blog.csdnimg.cn/direct/f3029e457c42495aa2a0ae78e9e6049b.png


  • 菱形继承的典例:

https://i-blog.csdnimg.cn/direct/1bf3a1f852124fdeadb1647b43382ec3.png


  • 菱形继承问题: 1.羊继承了动物数据,驼同样继承了动物数据,当草密码使用数据时,救就会产生二义性 2.草泥马继承来自动物类的两份数据,但其实我们清楚,这份数据我们只需要一份就够了
#include<iostream>
using namespace std;

class Animal//动物类
{
public:

    int m_Age;
};

class Sheep : public Animal//羊类
{
public:

};

class Tuo : public Animal//驼类 
{
public:

};

class SheepTuo : public Sheep, public Tuo {
public:

};
void test() {
    SheepTuo st;
    st.m_Age = 100;
}
int main(){test();return 0;}
  • 二义性:出现菱形继承,两个父类拥有相同数据,需加以作用域区分

https://i-blog.csdnimg.cn/direct/40f5cb068a024fa9bff6d06f64caf0ae.png

void test() {
    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;
}

https://i-blog.csdnimg.cn/direct/848406361fa7464da23927476bd050c4.png


  • 年龄这份数据,我们知道只要有一份就够了,但菱形继承却导致这份数据有两份,造成资源上的浪费

https://i-blog.csdnimg.cn/direct/70220900b8b64bb3bb2c028cb45482d8.png


6.8虚继承 — 解决菱形继承问题

  • 继承前加入关键字:virtual ---> 变为虚继承,而其父类被称为虚基类
class Animal{
public:

    int m_Age;
};

//class Sheep : virtual public Animal就是虚继承
class Sheep : virtual public Animal//此时Animal就被称为虚基类
{
public:

};

class Tuo : virtual public Animal{
public:

};

class SheepTuo : public Sheep, public Tuo {
public:

};
void test() {
    SheepTuo st;
    st.Sheep::m_Age = 100;
    st.Tuo::m_Age = 200;
    cout << "st.m_Age == " << st.m_Age << endl;
    cout << "st.Sheep::m_Age == " << st.Sheep::m_Age << endl;
    cout << "st.Tuo::m_Age == " << st.Tuo::m_Age << endl;
}
int main(){test();return 0;}

https://i-blog.csdnimg.cn/direct/82ef55acbf1e4e1d81dab47d7fcef233.png

  • m_Age被归为同一份数据,且st.m_Age也不再产生二义性(因为只有一份m_Age数据)。

https://i-blog.csdnimg.cn/direct/981e5f1eb21a48fca06037d7b67fb1fb.png

https://i-blog.csdnimg.cn/direct/653e03f6ff0e4be397f39fc84bbfd8b9.png


总结:

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


文章来源: https://blog.csdn.net/2401_87692970/article/details/146368244
版权声明: 本文为博主原创文章,遵循CC 4.0 BY-SA 知识共享协议,转载请附上原文出处链接和本声明。


...全文
30 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

5,411

社区成员

发帖
与我相关
我的任务
社区描述
微软技术社区为中国的开发者们提供一个技术干货传播平台,传递微软全球的技术和产品最新动态,分享各大技术方向的学习资源,同时也涵盖针对不同行业和场景的实践案例,希望可以全方位地帮助你获取更多知识和技能。
windowsmicrosoft 企业社区
社区管理员
  • 山月照空舟
  • 郑子铭
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

微软技术社区为中国的开发者们提供一个技术干货传播平台,传递微软全球的技术和产品最新动态,分享各大技术方向的学习资源,同时也涵盖针对不同行业和场景的实践案例,希望可以全方位地帮助你获取更多知识和技能。

予力众生,成就不凡!微软致力于用技术改变世界,助力企业实现数字化转型。

试试用AI创作助手写篇文章吧