前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++类与对象--继承(详细讲解)

C++类与对象--继承(详细讲解)

作者头像
用户11039529
发布2024-03-25 15:38:09
1230
发布2024-03-25 15:38:09
举报
文章被收录于专栏:算法学习日常

继承

一、继承的基础介绍

继承是面向对象三大特征之一

有些类和类之间存在特殊关系,如:

我们可以发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的个性。这个时候我们就可用继承的技术减少重复代码。 继承的基本语法 如很多网站中都有公共的头部,公共的底部,公共的左侧列表,只有中心内容不同,接下来用普通写法和继承写法来实现网页(我以CSDN的网页为例)中的内容,看一下继承存在的意义以及好处

普通版网页和继承版网页的区别

普通版网页

代码语言:javascript
复制
//普通版网页
class ZhuYe
{
public:
    void head()
    {
        cout << "博客  下载  学习  社区  C知道    GitCode InsCode" << endl;
    }
    void foot()
    {
        cout << "用户名 关注 收藏" << endl;
    }
    void left()
    {
        cout << "原创 周排名  总排名 访问" << endl;
    }
};
​
class Czhidao
{
public:
    void head()
    {
        cout << "博客  下载  学习  社区  C知道    GitCode InsCode" << endl;
    }
    void foot()
    {
        cout << "用户名 关注 收藏" << endl;
    }
    void context()
    {
        cout << "请输入你的问题" << endl;
    }
    void left()
    {
        cout << "原创 周排名  总排名 访问" << endl;
    }
​
};
​
​
class SheQu
{
public:
    void head()
    {
        cout << "博客  下载  学习  社区  C知道    GitCode InsCode" << endl;
    }
    void foot()
    {
        cout << "用户名 关注 收藏" << endl;
    }
    void context()
    {
        cout << "与我相关 最新发布 最新回复 最热 有活动 有问题" << endl;
    }
    void left()
    {
        cout << "原创 周排名  总排名 访问" << endl;
    }
​
};
​
void test01()
{
    ZhuYe a;
    a.head();
    a.foot();
    a.left();
    cout << "-------------------------" << endl;
​
    Czhidao b;
    b.head();
    b.context();
    b.foot();
    b.left();
    cout << "-------------------------" << endl;
​
    SheQu c;
    c.head();
    c.context();
    c.foot();
    c.left();
}
​
int main()
{
    test01();
    return 0;
}

其实会发现这个代码中有大量重复代码,这样子的代码是很Low的,也不符合C++是面向对象的语言的标准,而且会使得代码量加大,这个在企业开发中是一定要杜绝

继承版网页

代码语言:javascript
复制
//继承版网页
class ZhuYe
{
public:
    void head()
    {
        cout << "博客  下载  学习  社区  C知道    GitCode InsCode" << endl;
    }
    void foot()
    {
        cout << "用户名 关注 收藏" << endl;
    }
    void left()
    {
        cout << "原创 周排名  总排名 访问" << endl;
    }
};
​
class Czhidao : public ZhuYe
{
public:
    void context()
    {
        cout << "请输入你的问题" << endl;
    }
};
​
​
class SheQu : public ZhuYe
{
public:
    void context()
    {
        cout << "与我相关 最新发布 最新回复 最热 有活动 有问题" << endl;
    }
};
​
void test01()
{
    ZhuYe a;
    a.head();
    a.foot();
    a.left();
    cout << "-------------------------" << endl;
​
    Czhidao b;
    b.head();
    b.context();
    b.foot();
    b.left();
    cout << "-------------------------" << endl;
​
    SheQu c;
    c.head();
    c.context();
    c.foot();
    c.left();
}
​
int main()
{
    test01();
    return 0;
}

以上是继承版代码,可以看出来他将重复的部分给删掉了,这里用了继承这个语法,现在我来讲讲继承的语法

语法

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

子类:又称派生类

父类:又称基类

派生类中的成员包括两大部分 一类是从基类继承过来的,一类是自己增加的成员 从基类继承过来的表现其共性,而新增的成员体现了个性

二、继承方式

三种继承方式

公共继承

保护继承

**私有继承

从图可知:

父类中的私有成员,不管子类以哪种方式继承,都不可访问

就像父亲的银行卡密码,就算你是他儿子,他也不会告诉你,因为那是他的私有财产

三、继承中的对象模型

问题,从父类继承过来的成员,哪些属于子类中?

答案:

1.父类所有非静态成员属性都会被子类继承下去 2.父类中的成员属性是被编译器给隐藏了,因此是访问不到的,但是确实被继承下去了,大家可以用以下代码检测一下

代码语言:javascript
复制
class A
{
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};
​
class B : public A
{
public:
    int m_D;
};
​
int main()
{
    B test;
    cout << sizeof(test) << endl;
    return 0;
}

以上代码的答案是:16,虽然B只能访问A的m_A,m_B,加上自己的m_D,只有12个字节数,但是父类的m_C只是不可访问,不等于其不存在,因此,可以看出来子类继承了父类中所有非静态成员

四、继承中构造和析构函数

子类继承父类后,当创建子类对象,也会调用父类的构造函数

问题:父类和子类的构造和析构函数顺序谁先谁后?

大家可以通过以下代码来看看:

代码语言:javascript
复制
class A
{
public:
    A()
    {
        cout << "父类构造函数执行" << endl;
    }
    ~A()
    {
        cout << "父类析构函数函数执行" << endl;
    }
​
};
​
class B : public A
{
public:
    B()
    {
        cout << "子类构造函数执行" << endl;
    }
    ~B()
    {
        cout << "子类析构函数函数执行" << endl;
    }
​
};
​
int main()
{
    B test;
    return 0;
}

会发现继承中的构造和析构顺序如下: 先构造父类,再构造子类,析构的顺序与构造的顺序相反

五、继承同名成员的处理方式

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

访问同名成员:

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

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

作用域写法:

对象名.父类::成员名

大家可以看看以下代码来理解

代码语言:javascript
复制
class A
{
public:
    int m_A = 10;
};
​
class B : public A
{
public:
    int m_A = 20;
};
​
​
int main()
{
    B test;
    cout << test.m_A << endl;
    cout << test.A::m_A << endl;
​
    return 0;
}

如果子类中出现与父类同名成员函数;要访问就要加作用域

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

继承同名静态成员在子类对象上如何访问? 静态成员和非静态成员出现同名,处理方式一致

访问同名成员:

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

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

不过,静态成员有两种方式访问

通过对象

通过类名

第一种方法与非静态成员一样的方式,我就不过多赘述,我来讲讲第二种方式

为什么能用类名访问静态成员?

因为静态成员与静态成员函数在内存中都只有一份,所以所有对象都能直接访问他,因此只需要类名就能知道它具体的值

类名访问语法(以以下代码的访问为例)

代码语言:javascript
复制
class A
{
public:
    static int m_A;
};
int A::m_A = 10;
​
class B : public A
{
public:
    static int m_A;
};
int B::m_A = 20;
​
int main()
{
    cout << B::m_A << endl;
    cout << B::A::m_A << endl;
​
    return 0;
}

插入静态成员知识点:

静态成员:

类型前加static

类内声明,类外初始化,一定要初始(因为静态变量放在全局区,全局区在编译阶段就分配内存)

静态成员函数:

返回类型前加static

只可访问静态变量

总结:

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

七、多继承语法

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

语法:

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

多继承可能会引发父类有同名成员出现,要加作用域区分,因为容易出错,所以C++实际开发中不建议用多继承,因此不作过多介绍

八、菱形继承

概念:

两个派生类继承同一个基类 又有某个类同时继承两个派生类

以下例子虽然不符合事实动物的来源,但是有利于理解,大家就理解概念就好

菱形继承问题:

1.羊继承了动物的数据,驼同样继承了动物的数据,当羊驼使用数据时,就会产生二义性

代码语言:javascript
复制
class Animal
{
public:
    int m_Age;
};
​
class Sheep:public Animal
{
public:
    int m_Age;
};
​
class Tuo :public Animal
{
public:
    int m_Age;
};
​
class SheepTuo :public Sheep, public Tuo
{
public:
    int m_Age;
};
​
​
void test()
{
    SheepTuo st;
    st.Sheep::m_Age = 20;
    st.Tuo::m_Age = 10;
}
​
int main()
{
    test();
    return 0;
}

以上代码就有二义性:羊驼的年龄应该是和羊一样为20岁,还是应该和驼一样为10岁呢?

2.羊驼继承的动物的数据继承了两份,这份数据我们只需要一份就行

解决办法:

利用虚继承,解决菱形继承的问题

虚继承语法:

在继承之前加上关键字virtual变成虚继承

代码语言:javascript
复制
class Animal
{
public:
    int m_Age;
};
​
class Sheep:virtual public Animal
{
public:
    int m_Age;
};
​
class Tuo :virtual public Animal
{
public:
    int m_Age;
};
​
class SheepTuo :public Sheep, public Tuo
{
public:
    int m_Age;
};
​
​
void test()
{
    SheepTuo st;
    st.Sheep::m_Age = 20;
    st.Tuo::m_Age = 10;
}
​
int main()
{
    test();
    return 0;
}

这时你

代码语言:javascript
复制
cout << st.m_Age << endl;
cout << st.Sheep::m_Age << endl;
cout << st.Tuo::m_Age << endl;

都是等于10,因为三者在实际上时共用了一个数据,这里涉及了虚拟基类指针和虚拟基类表,这里涉及开发命令页的操作来展现,大家感兴趣的可以自行查找相关资料

总结:

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 继承
    • 一、继承的基础介绍
      • 普通版网页和继承版网页的区别
      • 语法
    • 二、继承方式
      • 三种继承方式
    • 三、继承中的对象模型
      • 四、继承中构造和析构函数
        • 五、继承同名成员的处理方式
          • 访问同名成员:
          • 作用域写法:
        • 六、继承同名静态成员的处理方式
          • 访问同名成员:
          • 为什么能用类名访问静态成员?
          • 静态成员:
          • 静态成员函数:
          • 总结:
      • 七、多继承语法
        • 语法:
        • 八、菱形继承
          • 概念:
            • 菱形继承问题:
              • 解决办法:
                • 虚继承语法:
                • 总结:
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档