IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 类和对象-继承 -> 正文阅读

[C++知识库]类和对象-继承

继承——基本语法

class 子类(派生类):继承方式 父类(基类)
继承方式:
1.public 公有继承
2.protected 保护继承
3.private 私有继承

下面来理解一下这张图片:
在这里插入图片描述
首先声明一点:无论是哪一种继承方式,子类都无法访问到父类的私有成员!!!
1.公有继承:父类中的public权限成员在子类中仍然为public权限成员。
父类中的protected权限成员在子类中仍然为protected权限成员。
且子类在类外访问不到protected权限内容。
2.保护继承:父类中的public与protected权限成员在子类中都变成了protected权限成员,在类外访问不到。
3。私有继承:父类中的public和protected权限成员都变成了private成员,在类外都不可访问。

继承中的对象模型

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

父类中所有非静态成员属性都会被子类继承,而父类中的所有私有成员属性是被编译器给隐藏了,因此访问不到,但确实是被继承下来了.  
下面我们来看一个案例:
在这里插入代码片

```#include<iostream>
using namespace std;

class Base
{
public:
    int m_A = 10;
protected:
    int m_B = 20;
private:
    int m_C = 30;
};
class Son : public Base
{
public:
    int m_D = 40;
};
void test01()
{
    Son s;
    cout<<"s所占内存空间的大小 = "<<sizeof(s)<<endl;

}
int main()
{
    test01();
    return 0;
}

下面我们来看一下结果:
在这里插入图片描述
结果显示所占内存空间为:16
所以很显然父类的所有成员都被继承下来了。

构造和析构的顺序

下面我们来看一个案例:

在这里插入代码片
#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;
    }
};
void test01()
{
    Son s;
}
int main()
{
    test01();
    return 0;
}

我们再来看一下结果:
在这里插入图片描述
可以看到新进行父类的构造函数,在进行子类的构造函数农户。
构造函数与析构函数的顺序相反:先构造的后析构,后构造的先析构。

同名成员处理

当子类中出现与父类一样的同名成员是,怎样通过子类对象,来访问子类或父类的同名成员。

1.如果是访问子类同名成员,则直接访问;
2.如果是访问父类同名成员,则加上作用域;

下面我们来看一下代码;

在这里插入代码片
#include<iostream>
using namespace std;

class Base
{
public:
   int m_A = 10;
   void func()
   {
       cout<<"调用Base的函数"<<endl;
   }
};
class Son : public Base
{
public:
    int m_A = 20;
    void func()
    {
        cout<<"调用Son的函数"<<endl;
    }
};
void test01()
{
    Son s;
    s.Base::func();
    cout<<"Base的m_A = "<<s.Base::m_A<<endl;

    s.func();
    cout<<"Son的s.m_A = "<<s.m_A<<endl;
}
int main()
{
    test01();
    return 0;
}

图片中的父类和子类含有同名函数和同名数据成员
在这里插入图片描述
子类则直接调用,如下在这里插入图片描述
父类则需要加上作用域,如下在这里插入图片描述
好,接下来我们看一下上面代码的运行结果:在这里插入图片描述
如果子类与父类有同名成员,则子类的同名成员会将父类的隐藏,如若想进行访问,就需要加上作用域;

同名静态成员处理

首先我们来看一下
静态成员变量属性:
1.所有对象都共享同一份数据;
2.编译阶段就分配内存;
3.类内声明,类外初始化;
静态成员函数属性:
1.只能访问静态成员变量,不能访问非静态成员变量;
2.所有对象都共享同一份;

与非静态同名成员处理方式一样,也是要加上作用域才能访问父类同名函数成员;
请看下面代码:

在这里插入代码片
#include<iostream>
using namespace std;

class Base
{
public:
   static int m_A ;//类内声明
   static void func()
   {
       cout<<"调用Base的静态成员函数"<<endl;
       cout<<"Base的m_A = "<<m_A<<endl;
   }
};
    int Base:: m_A = 10;//类外初始化
class Son : public Base
{
public:
    static int m_A ;
    static void func()
    {
        cout<<"调用Son的静态成员函数"<<endl;
        cout<<"Son的m_A = "<<m_A<<endl;
    }
};
    int Son:: m_A = 20;
void test01()
{
    Son s;
    s.func();

    s.Base::func();
}
int main()
{
    test01();
    return 0;
}

结果:在这里插入图片描述
可以看到即使是静态同名函数成员,在访问子类同名成员时直接利用对象直接访问即可,而父类需要加上作用域才能够访问。
我们不光能够使用类对象进行访问,同时我们也可以使用类名进行访问静态同名函数成员,这一点就体现静态与非静态的出入;

在这里插入代码片
#include<iostream>
using namespace std;

class Base
{
public:
   static int m_A ;//类内声明
   static void func()
   {
       cout<<"调用Base的静态成员函数"<<endl;
       cout<<"Base的m_A = "<<m_A<<endl;
   }
};
    int Base:: m_A = 10;//类外初始化
class Son : public Base
{
public:
    static int m_A ;
    static void func()
    {
        cout<<"调用Son的静态成员函数"<<endl;
        cout<<"Son的m_A = "<<m_A<<endl;
    }
};
    int Son:: m_A = 20;
void test01()
{
    Son::func();

    Son::Base::func();
}
int main()
{
    test01();
    return 0;
}

差别就在于:
在这里插入图片描述
其中子类类名直接加作用域比较好理解;
而访问父类中:Son::表示通过类名方式进行访问;Base::表示作用域;
总结来说:Son::Base::func();
调用父类中的同名func()函数;

多继承语法

class 子类名 : 继承方式 父类1,继承方式 父类2 …
下面看一段代码:

在这里插入代码片
#include<iostream>
using namespace std;

class Base1
{
public:
    int m_A = 10;
};
class Base2
{
public :
    int m_B = 20;
};
class Son : public Base1,public Base2
{
public:
    int m_C = 30;
    int m_D = 40;
};
void test01()
{
    cout<<"Son类所占内存大小:"<<sizeof(Son)<<endl;
}
int main()
{
    test01();
    return 0;
}

结果:
在这里插入图片描述
其中Son所占字节为 16 ,说明继承了Base1和Base2的数据成员。
但是多继承同时也会带来麻烦,所以实际运用中分不推荐使用,例如
在这里插入图片描述
两个父类中都含有m_A,那么子类在继承两个父类时,便会出现二义性,不知道该使用哪一个。
若想访问,则需要加上作用域

在这里插入代码片
void test01()
{
    Son s;
    cout<<"Base1中的m_A = "<<s.Base1::m_A<<endl;
    cout<<"Base2中的m_A = "<<s.Base2::m_A<<endl;
}

在这里插入图片描述
这样便能够访问到Base1和Base2的m_A;

菱形继承

菱形继承也可以成为钻石继承
在这里插入图片描述

在这里插入代码片
#include<iostream>
using namespace std;

class Base
{
public:
    int m_Age = 10;
};
class A:virtual public Base{};
class B:virtual public Base{};
class D:public A,public B
{

};
void test01()
{
    D d;
    cout<<"d.m_Age = "<<d.m_Age<<endl;
}
int main()
{
    test01();
    return 0;
}

因为类A和类B都会继承一份Base类中的m_Age,所以防止在类D多继承类A和类B时同时继承两份m_Age,而且这两份数据可能还不相等,所以在访问权限前加上virtual,使得类A和类B变成“虚基类”;
加上virtual后,其实在类A与类B中只是继承了vbptr指针,可以称为虚基类指针,
(v:vi rtual , b:base, ptr:pointer)
虚基类指针指向Base类中的数据;
这样就能利用虚继承解决菱形继承的问题。
总结:
1.菱形继承带来的主要问题是子类继承相同的数据,导致资源量费毫无意义。
2.利用虚继承来解决菱形继承的问题。

以上就是c++中的一些继承问题。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-15 18:05:37  更:2021-12-15 18:08:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 12:46:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码