C++深入探究继承的概念与使用

目录
  • 1、概念及定义
    • 1.1 概念
    • 1.2 定义
  • 2、class与struct的区别
  • 3、赋值兼容规则
  • 4、继承中的作用域问题
  • 5、派生类(子类)的默认成员函数
    • 5.1 构造函数
    • 5.2 拷贝构造函数
    • 5.3 赋值运算符重载
    • 5.4 析构函数
  • 6、基类中哪些成员被子类继承了
    • 6.1 成员变量
    • 6.2 成员方法
  • 7、友元函数被继承了吗

1、概念及定义

1.1 概念

继承主要的工作就是-----共性抽取

具体地讲:

①继承机制是面向对象程序设计使代码可以复用的最重要的手段;

②允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样实现的类称为派生类/子类。基于实现该类的原有类称为基类/父类

③继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。(比如:animal—>dog---->kinds of dogs)

④继承是类层次设计的复用

1.2 定义

定义方式:class 派生类:继承方式 基类

继承方式可以是 public、protected、private三种,他们在继承基类时,所具有特性以及表现出的结果也有所不同,具体如下:

以public的方式继承基类

结论:

在public的继承方式下:

①父类中的成员变量的访问权限,到子类中不会发生改变

②父类中的私有访问权限的变量在子类中不可见(不能直接被访问)

问题:类在设计的时候,访问权限应该如何选择?

应该遵循以下3点原则:

以protected的方式继承基类

结论:

在protected的继承方式下:

①基类中public修饰的 成员在子类中访问权限为protected

②基类中protected修饰的成员在子类中的访问权限依旧是protected

③父类中的private访问权限的变量在子类中不可见(不能直接被访问)

以private的方式继承基类

结论:

在private的继承方式下:

①基类中public修饰的 成员在子类中访问权限为private

②基类中protected修饰的成员在子类中的访问权限为private

③父类中的private访问权限的变量在子类中不可见(不能直接被访问)

上面详细分析了每一种个情况,下面我们针对上面的结论进行汇总:

注意:

1、基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。

2、基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的

3、在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。

2、class与struct的区别

主要有以下3点区别:

3、赋值兼容规则

前提:一定在public的继承方式下才满足

可以直接使用子类对象给父类对象赋值,反过来不行

这个很好理解,具体和可以通过两个方面理解:

①子类和父类的关系是is–a的关系,因此使用子类给父类赋值时可以的

②从对象模型来说。

对象模型可以简单理解为成员变量在内存中的布局情况;

可以使用基类的指针指向子类的对象,反过来不行

如果一定要指向,必须强转,不推荐,仅仅是能通过编译,但是在使用的时候可能会造成程序崩溃

分析如下:

可以使用基类的引用去引用子类对象,反过来不行

引用在底层本质上就是使用指针实现的,因此它和指针的理解思路是一致的,这里就不再赘述。

4、继承中的作用域问题

明确:派生类和基类隶属于不同的作用域

那么,现在有这样一种情况:

基类和派生类中出现了同名的成员变量或成员方法。这种情况要如何去理解呢? 首先,他一定不是函数重载,因为函数重载的前提必须是在同一作用域。 其实它就是我们本模块要介绍的----同名隐藏(重定义)问题

基类和派生类中出现同名的成员时,会有如下问题的存在:

那么,该如何解决呢?

只需要在访问的时候加上 基类名称和作用域限定符即可,这样做的目的是明确告诉编译器被调用成员所处的作用域

建议:一般情况下,在继承体系中最好不要定义同名的成员

5、派生类(子类)的默认成员函数

5.1 构造函数

主要取决于基类的情况,分为两大类进行讨论:

基类没有显式定义任何构造函数

子类可以提供构造函数,也可以不提供构造函数 是否提供根据子类中完成的功能或者具体情况决定

基类显式定义了构造函数

①基类的构造函数是无参或者全缺省的

子类可以提供构造函数,也可以不提供构造函数

是否提供根据子类中完成的功能或者具体情况决定

②基类的构造函数是非默认构造函数

子类必须要定义自己的构造函数

在子类构造函数初始化列表位置显式调用基类的构造函数(完成从基类中继承下来的成员的初始化工作)

基类和子类构造函数的调用先后顺序是怎样的?

把握一点:

创建那个类的对象,编译器就会调用这个类的构造函数

例如:创建子类对象,本质上调用的是子类的构造函数,但是在子类的构造函数的初始化列表处会调用基类的构造方法来初始化从基类继承下来的对象。然后再去执行子类构造函数的函数体。 因此,从结果上来看是基类对象的构造函数先执行完毕,子类构造函数后执行完毕。

5.2 拷贝构造函数

取决于基类的情况,主要分为两类:

基类的拷贝构造函数未定义

子类的拷贝构造函数可定义可不定义,根据子类的实际情况决定

基类的拷贝构造函数定义了

子类也需要定义拷贝构造函数,并且需要在子类的拷贝构造函数初始化列表的位置显式调用基类的拷贝构造函数

5.3 赋值运算符重载

1.基类的赋值运算符重载未定义

子类可定义可不定义

2.基类的赋值运算符重载显式定义了

子类也需要定义,分为两个大的步骤:

①调用基类的赋值运算符重载给基类部分成员赋值base::operator= (d);

②给子类自己新增的部分进行赋值

注意:基类的operator= 与子类自己的 operator= 构成了同名隐藏,因此要加作用域限定符指定调用基类的operator=,否则默认调用子类自己的operator=,就会陷入无限递归

正确示范:

错误示范:

5.4 析构函数

编译器将子类的析构函数编译完成之后,会自动在子类析构函数的最后一条语句之后插入一条调用基类析构函数的汇编语句call ~Base();!

问题:基类和子类析构函数调用先后顺序?

6、基类中哪些成员被子类继承了

6.1 成员变量

普通成员变量,全部被继承!

这个我们在本文的1.2 定义这个模块已经全部验证!

静态成员变量也被继承了

注意:静态成员变量在整个继承体系中只有一份

验证:通过静态变量来记录创建对象的个数

class Base
{
public:
	Base(int a,int b)
	{
		_a = a;
		_b = b;
		++_count;
	}
	Base(const Base& b)
	{
		_a = b._a;
		_b = b._b;
		++_count;
	}
	Base& operator=(const Base& b)
	{
		_a = b._a;
		_b = b._b;
		return *this;
	}
	~Base()
	{
		cout << "Base::~Base()" << endl;
		--_count;
	}
public:
	int _a;
	int _b;
	static int _count;
};
int Base::_count = 0;
class Derived : public Base
{
public:
	Derived()
		:Base(1,2)
	{
	}
	Derived(int a,int b,int c)
		:Base(a,b)
		, _c(c)
	{}
	Derived(const Derived& d)
		:Base(d)
	{
		_c = d._c;
	}
	Derived& operator=(const Derived& d)
	{
		Base::operator=(d);
		_c = d._c;
		return *this;
	}
	~Derived()
	{
		cout << "Derived::~Derived()" << endl;
	}
public:
	int _c;
};
void Test()
{
	cout << &Base::_count << endl;
	cout << &Derived::_count << endl;
}

6.2 成员方法

普通成员方法,被子类继承了。

前面的代码均有体现,这里不再验证~

静态成员方法—也被子类继承了

验证:

7、友元函数被继承了吗

明确:友元函数不是类的成员函数,他只是在一个类中进行声明,目的是打破类的封装性去访问原本外部不可访问的成员。

这个问题很好测试,我们只需要为子类提供一个友元函数去访问父类中的protected访问权限的成员变量。让一个子类继承自父类,然后测试在父类中声明的友元函数是否能够访问子类中的protected/private成员变量即可!

如果可以访问,那就说明 友元函数也会被继承下来。

如果不可以访问,那就说明友元函数不会被继承下来

直接上例子:

Test函数测试结果:

结论:友元函数不能被继承!

本篇文章到这里就结束了,感觉有所帮助的读友,可以转发分享给身边的朋友并留下你们的足迹!

下篇我们讲讲C++中一些不同的继承体系~,我们下篇再见!

到此这篇关于C++深入探究继承的概念与使用的文章就介绍到这了,更多相关C++继承内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++类的继承和派生及指针安全引用

    目录 一.继承和paisheng 1.继承和派生的基础概念 2.如何定义子类 3.吸收和改造父类成员.添加新成员 二.指针引用 1.指针和引用的异同 2.指针的安全隐患 一.继承和paisheng 1.继承和派生的基础概念 继承指从现有类获得其特性,派生指从已有类产生新的类.原有的类称为基类或父类,新生的类称为子类或派生类.当子类只有一个父类时称为单继承,当子类含有多个父类时称为多继承.如果基类A直接参与了派生类B的派生过程,那么A类称为直接基类:基类的基类称为间接基类. 2.如何定义子类 定义

  • C++详细讲解继承与虚继承实现

    目录 继承的概念及定义 概念: 定义: 继承关系和访问限定符 总结 基类和派生类对象赋值转换 继承中的作用域 派生类的默认成员函数 继承与友元 继承与静态成员 复杂的菱形继承及菱形虚拟继承 虚继承原理 继承的总结 继承的概念及定义 概念: 继承机制是面向对象程序设计为了提高代码复用率的一种手段,它可以保持原类特性的基础上进行拓展,简单来说继承是类层次的复用. 接下来我们来看一个简单的继承 class Person { public: void Print() { cout<<"nam

  • C++ 超详细梳理继承的概念与使用

    目录 继承的概念及定义 继承的概念 继承定义 定义格式 继承关系和访问限定符 继承基类成员访问方式的变化 基类和派生类对象赋值转换 继承中的作用域 派生类的默认成员函数 继承与友元 继承与静态成员 复杂的菱形继承及菱形虚拟继承 菱形继承 虚拟继承解决数据冗余和二义性的原理 继承的总结和反思 继承的概念及定义 继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类.继承呈现了面向对象程序设计的层次结构,体

  • C++深入探究不同的继承体系

    目录 单继承 多继承 菱形继承 概念 存在的问题 解决方案 菱形虚拟继承 虚拟继承 什么是虚拟继承 内存层面理解虚拟继承 虚拟继承和普通单继承的区别 虚拟继承+菱形继承 声明: 本文的测试环境为Windows平台下的VS2013环境 单继承 一个子类只有一个父类.这类继承方式是最为简单的.具体可以参考继承 上篇中的例子.这里给出简单的图示即可: 多继承 一个子类有两个或两个以上的直接父类,这样的继承关系称之为多继承. 图解: 注意事项:  ①在多继承的场景下,建议在继承的每一个基类名称前都加上继

  • 详解C++中单继承与多继承的使用

    目录 前言 1.继承的概念和定义 (1)继承的概念 (2)继承的定义方法 (2)继承后子类的成员类型 2.基类与派生类的赋值转换 (1)派生类赋值给基类 (2)基类给派生类 3.继承中的作用域 (1)隐藏的概念 (2)例题 4.派生类的默认成员函数 (1)默认生成的成员函数 (2)自己写 5.友元与静态成员 6.多继承 (1)概念 (2)复杂的菱形继承 (3)虚继承解决菱形继承问题 (4)虚继承的原理 7.继承与组合 (1)两者区别 (2)继承与组合的区别 (3)使用情况 8.总结 前言 C++

  • C++类继承时的构造函数

    前言: 子类需要编写自己的构造函数和析构函数,需要注意的是,子类只负责对新增的成员进行初始化和扫尾编写构造和析构函数,父类成员的初始化和扫尾工作由父类的构造函数和析构函数完成. 无论何种类型的继承方式,子类都无权访问父类的所有成员,所以子类对父类的初始化需要父类的构造函数完成.此时,子类的构造函数必须提供父类构造函数所需的参数. 子类构造函数的语法如下: 子类::子类(全部参数表):父类1(父类1参数表),父类2(父类2参数表)      ...对象成员1(对象成员1参数表),对象成员2(对象成

  • C++ 多继承详情介绍

    C++支持多继承,即允许一个类同时继承多个类. 关于多继承,一直以来争议不断,有一部分人认为多继承会带来大量的问题,为了解决这些问题会使得语言本身变得非常复杂,因此应当避免.另外一派认为多继承在某些场景下可以起到非常关键的作用,应当予以支持. 关于多重继承是好是坏,这是一个非常复杂的问题,网上历来争议不断.因此不过多阐述,感兴趣的同学可以查阅一下相关资料.仅仅从实际支持来看,目前市面上大部分的语言包括Java仅支持单继承,只有C++等少数语言支持多继承.这和C++的理念也有关,即认定不能通过减少

  • C++图文并茂讲解继承

    目录 一.生活中的例子 二.惊艳的继承 三.继承的意义 四.小结 一.生活中的例子 组合关系∶整体与部分的关系 下面看一个组合关系的描述代码: #include <iostream> #include <string> using namespace std; class Memory { public: Memory() { cout << "Memory()" << endl; } ~Memory() { cout <<

  • C++的继承特性你了解吗

    目录 导语: 继承作用 继承的结果 继承方式 子类构造 赋值兼容规则/向上转换/内存切片 多继承 虚拟继承 总结 导语: C++是对C语言的优化和改进,C++之所以优秀的点在于它的特性:抽象.封装.继承和多态. 本章总结继承的规则和特性,都是干货,与读者共同学习. 继承作用 代码的复用 子类继承父类,可以理解为,将父类的代码拷贝一份到子类中,达到子类可以调用父类方法的目的. 那为什么是可以理解而不是就是呢? 是因为有几个东西是不可以拷贝的,比如,父类的拷贝和析构方法,友元和静态成员. 友元关系是

  • C++深入探究继承的概念与使用

    目录 1.概念及定义 1.1 概念 1.2 定义 2.class与struct的区别 3.赋值兼容规则 4.继承中的作用域问题 5.派生类(子类)的默认成员函数 5.1 构造函数 5.2 拷贝构造函数 5.3 赋值运算符重载 5.4 析构函数 6.基类中哪些成员被子类继承了 6.1 成员变量 6.2 成员方法 7.友元函数被继承了吗 1.概念及定义 1.1 概念 继承主要的工作就是-----共性抽取 具体地讲: ①继承机制是面向对象程序设计使代码可以复用的最重要的手段; ②允许程序员在保持原有类

  • java继承的概念及案例解析

    这篇文章主要介绍了java继承的概念及案例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 package java09; //定义一个员工类 public class Employee { public void method(){ System.out.println("方法执行!!!"); } } package java09; //定义一个教师类 public class Teacher extends Employee {

  • C++中继承的概念和定义

    目录 1.继承的概念及定义 1.1继承的概念 1.2继承的定义格式 1.3继承基类成员访问方式的变化 (1)公有继承 (2)保护继承 (3)私有继承 1.4总结 2.基类和派生类对象赋值转换 3.继承中的作用域 总结 1.继承的概念及定义 1.1继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类.继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程.以前我们接触的复用都是函数复用,继承

  • C++数据结构继承的概念与菱形继承及虚拟继承和组合

    目录 继承的概念 继承的定义 基类和派生类对象之间的赋值转换 继承中的作用域 派生类的默认成员函数 继承中的两个小细节

  • 一文搞懂C++中继承的概念与使用

    目录 前言 继承概念及定义 继承概念 继承定义 继承方式 父类和子类对象赋值转换 继承中的作用域 派生类的默认成员函数 派生类的友元与静态成员 继承关系 单继承 多继承 菱形继承 前言 我们都知道面向对象语言的三大特点是:**封装,继承,多态.**之前在类和对象部分,我们提到了C++中的封装,那么今天呢,我们来学习一下C++中的继承. 继承概念及定义 继承概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能

  • JS继承 笔记

    JS继承 JavaScript中没有类的概念,与类相关的继承的概念更是无从谈起,但是我们可以通过特殊的语法来 模拟面向对象语言中的继承. 在JS中模拟继承有多种方式,其中寄生组合模式是一种比较容易简单的模拟继承模式,下面我们就来 介绍一下用寄生组合模式模拟继承. JS的继承包括属性的继承和方法的继承,他们分别通过不同的方法来实现. 1属性的继承 属性的继承通过改变函数的执行环境来实现的.而改变函数的执行环境可以使用call()和apply()两种 方法来实现. 我们首先创建一个Animal"类&

  • javascript 面向对象编程基础:继承

    我们看到这里继承的概念是多么的直白,"拷贝一个类的prototype 到另外一个类",好,Code is cheap,看代码: function class1() { } function class2() { } class2.prototype = class1.prototype; class2.moreProperty1 = " class 2 additional string " ; class2.moreMethod1 = function () {

  • C++ 的三种访问权限与三种继承方式

    三种访问权限 我们知道C++中的类,有三种访问权限(也称作访问控制),它们分别是public.protected.private.要理解它们其实也很容易,看下面了一个例子. 父类: class Person { public: Person(const string& name, int age) : m_name(name), m_age(age) { } void ShowInfo() { cout << "姓名:" << m_name <&l

随机推荐