初识C++之继承

1、何为继承

  C++中所谓继承,就是在一个已存在类的基础上创建一个新的类,新类获得已存在类的部分特性(为什么是部分特性,后面会讲到)。已存在类被称为基类(Base Class)或父类(Father Class),新建类被称为派生类(Derived Class)或子类(Son Class)。
这里写图片描述
继承的声明形式:
class 派生类名:[继承方式] 基类列表
{
  派生类增加的特性;
}

2、分类

继承在C++中有单继承与多继承的分类。
单继承:派生类有且只有一个基类
多继承:派生类两个或两个以上的基类
文字描述不过瘾,给出声明方法、模型及实例:
①简单的单继承声明方法、模型及实例:

class 派生类名:[继承方式] 基类名
{
  派生类增加的特性;
}
这里写图片描述

class Base           //父类/基类
{void Fun(){cout << "Base" << endl;}
protected:int data;
};class Derived1: public Base   //子类1/派生类1
{void Fun(){cout << "Derived1" << endl;}
private:int _data;
};class Derived2: public Base   //子类2/派生类2
{void Fun(){cout << "Derived2" << endl;}
private:int _data;
};

②简单的多继承声明方法、模型及实例:

class 派生类名:[继承方式] 基类列表
{
  派生类增加的特性;
}
这里写图片描述

class Base1
{void Fun(){cout << "Base" << endl;}
protected:int data1;
};class Base2
{void Fun(){cout << "Base" << endl;}
protected:int data2;
};class Derived: public Base1, public Base2
{void Fun(){cout << "Derived" << endl;}
private:int _data;
};

3、继承方式

先来回忆一下继承的声明形式:
class 派生类名:[继承方式] 基类列表
{
  派生类增加的特性;
}
  其中,继承方式分为共有(public)、私有(private)、保护(protected)之分,不同的继承方式会产生不同的继承效果(class默认继承方式是私有,struct默认继承方式是公有,但最好还是显示给出继承方式,这样会使程序更加清晰)。
  
继承方式的区别:
public:基类的非私有成员在派生类中的访问属性不变,基类的私有属性在派生类中存在但不可见。
protected:基类的非私有成员都称为派生类中的保护成员,基类的私有属性在派生类中存在但不可见。
private:基类的非私有成员都称为派生类中的私有成员,基类的私有属性在派生类中存在但不可见。
以上就是为什么说只是基类的部分特性在派生类中可见。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;class Base
{
public:Base(){}~Base(){}void ShowBase(){cout << "ShowBase" << endl;cout << "pri = " << pri << endl;cout << "pro = " << pro << endl;cout << "pub = " << pub << endl;}private:int pri;
protected:int pro;
public:int pub;
};class Derived :public Base
{
public:Derived(){}~Derived(){}void ShowDerived(){cout << "ShowDerived" << endl;cout << "_pri = " << _pri << endl;cout << "_pro = " << _pro << endl;cout << "_pub = " << _pub << endl;}private:int _pri;
protected:int _pro;
public:int _pub;
};int main()
{Base b;Derived d;b.ShowBase();d.ShowBase(); d.ShowDerived();cout << sizeof(Base) << endl;cout << sizeof(Derived) << endl;return 0;
}

输出结果:
这里写图片描述
对以上输出结果分析:
①为什么ShowBase会被调用两次
  ShowBase函数被调用了两次,是因为在Derived类中,继承有ShowBase函数,因为该函数在Base类中的属性是public,而且Derived类是以public方式继承自Base类的,所以该函数在Derived类中的属性仍为public,那么它就可以在类外被调用。
②为什么两个类的大小不同
  从输出结果中很明显的看到,Base类的大小为12,Derived类的大小为2。Base类的大小为12很好理解,因为它里面只有三个成员变量,且都为int类型,但为什么Derived是24呢,这就要牵扯到了继承方式特性,不管继承方式是什么,基类的成员变量的访问限定符是什么,基类的成员变量在派生类中都是存在的,只是有可见与不可见之分。
当我把继承方式修改为protected和private时,程序编译出错:
这里写图片描述

这里写图片描述
这就印证了我们上述的继承方式的特性,因为protected继承方式会把Base类中public访问限定符的BaseShow函数在Derived类中降级为protected访问限定符,同样,private继承方式会把Base类中public访问限定符的BaseShow函数在Derived类中降级为private访问限定符,而protected访问限定符和private访问限定符修饰的内容在类外是不可见的,强行调用肯定会报错,所以这儿会出现错误。

继承方式的特性:
①基类的private成员在派生类中是不能被访问的,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
②不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,基类的私有成员存在但是在子类中不可见(不能访问)。
③使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。

4、继承关系中构造函数和析构函数的调用
先给出一个例子来剖析:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;class Base
{
public:Base(){cout << "Base()" << endl;}~Base(){cout << "~Base()" << endl;}private:int pri;
protected:int pro;
public:int pub;
};class Derived :protected Base
{
public:Derived(){cout << "Derived()" << endl;}~Derived(){cout << "~Derived()" << endl;}private:int _pri;
protected:int _pro;
public:int _pub;
};int main()
{Base b;Derived d;return 0;
}

这里写图片描述

可以看到在派生类创建对象时,是先调用基类的构造函数,然后在代用自己的构造函数,对于析构函数就很好解释了,可以理解为栈的特性,先进后出。

5、继承关系的作用域
在C++的继承体系中,基类和派生类是属于两个不同作用域。当基类和派生类中有同名成员时,派生类成员将屏蔽基类对成员的直接访问(在派生类成员函数中,可以使用 基类名::基类成员来访问基类的同名成员),这就是所谓的同名隐藏,在派生类中的同名成员称为对基类同名成员的重定义。当然,实际中在继承体系里面最好不要定义同名的成员。
注意:因为基类和派生类是属于两个不同作用域,所以两个类中的同名函数不会构成函数重载。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;class Base
{
public:Base(int _pub = 0, int _pri = 1, int _pro = 2): pub(_pub), pri(_pri), pro(_pro){}~Base(){}void Display(){cout << "Base::Display" << endl;cout << "pub = " << pub << endl;cout << "pri = " << pri << endl;cout << "pro = " << pro << endl;}
public:int pub;
private:int pri;
protected:int pro;
};class Derived: public Base
{
public:Derived(int _pub = 0, int _pri = 1, int _pro = 2): pub(_pub), pri(_pri), pro(_pro){}~Derived(){}void Display(){cout << "Derived::Display" << endl;cout << "pub = " << pub << endl;cout << "pri = " << pri << endl;cout << "pro = " << pro << endl;}
public:int pub;
private:int pri;
protected:int pro;
};int main()
{Base b(1, 2, 3);Derived d(4, 5, 6);b.Display();d.Display();return 0;
}

这里写图片描述

根据上面例子的输出就可以看出来。

6、基类对象与派生类对象的赋值关系

①子类对象可以赋值给父类对象

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;class Base
{
public:Base(int bpub = 5, int bpri = 10, int bpro = 15): pub(bpub), pri(bpri), pro(bpro){}~Base(){}void Display(){cout << "Base::Display" << endl;cout << "pub = " << pub << endl;cout << "pri = " << pri << endl;cout << "pro = " << pro << endl;}public:int pub;
private:int pri;
protected:int pro;
};class Derived: public Base
{
public:Derived(int _dpub = 10, int _dpri = 20, int _dpro = 30): _pub(_dpub), _pri(_dpri), _pro(_dpro){}~Derived(){}void Display(){cout << "Derived::Display" << endl;cout << "_pub = " << _pub << endl;cout << "_pri = " << _pri << endl;cout << "_pro = " << _pro << endl;}public:int _pub;
private:int _pri;
protected:int _pro;
};int main()
{Base b(1, 2, 3);Derived d(4, 5, 6);b = d;b.Display();d.Display();return 0;
}

输出结果:
这里写图片描述
程序可以正常输出,但有些人可能会奇怪,为什么b里面存的是5、10、15,而不是4、5、6,其实原因很简单:
这里写图片描述
②父类对象不能赋值给子类对象
当我把上例中的“b = d;”换成“d = b;”时,编译器会报错:
这里写图片描述
还是像刚才那样分析:
这里写图片描述
③父类的指针/引用可以指向子类对象,但是无法使用不存在于基类只存在于派生类的元素。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;class Base
{
public:Base(int bpub = 5, int bpri = 10, int bpro = 15): pub(bpub), pri(bpri), pro(bpro){}~Base(){}void Display(){cout << "Base::Display" << endl;cout << "pub = " << pub << endl;cout << "pri = " << pri << endl;cout << "pro = " << pro << endl;}public:int pub;
private:int pri;
protected:int pro;
};class Derived: public Base
{
public:Derived(int _dpub = 10, int _dpri = 20, int _dpro = 30): _pub(_dpub), _pri(_dpri), _pro(_dpro){}~Derived(){}void Display(){cout << "Derived::Display" << endl;cout << "_pub = " << _pub << endl;cout << "_pri = " << _pri << endl;cout << "_pro = " << _pro << endl;}public:int _pub;
private:int _pri;
protected:int _pro;
};int main()
{Derived d(4, 5, 6);Base *b = &d;b->Display();return 0;
}

输出结果:
这里写图片描述
上分析图:
这里写图片描述
④子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)
我把上例中的

int main()
{Derived d(4, 5, 6);Base *b = &d;b->Display();return 0;
}

换成

int main()
{Base b(4, 5, 6);Derived *d = &b;d->Display();return 0;
}

程序会报错:
这里写图片描述
继续上图分析:
这里写图片描述

7、友元与继承
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。

8、继承与静态成员
基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;class Base
{
public:Base(int bpub = 5, int bsta = 0, int bpri = 10, int bpro = 15): pub(bpub), pri(bpri), pro(bpro){}~Base(){}void Display(){cout << "Base::Display" << endl;cout << "sta = " << sta << endl;}public:int pub;static int sta;
private:int pri;
protected:int pro;
};class Derived: public Base
{
public:Derived(int _dpub = 10, int _dpri = 20, int _dpro = 30): _pub(_dpub), _pri(_dpri), _pro(_dpro){}~Derived(){}void Display(){cout << "Derived::Display" << endl;cout << "sta = " << sta << endl;}public:int _pub;
private:int _pri;
protected:int _pro;
};int Base::sta = 10;int main()
{Base b;Derived d;b.Display();d.Display();return 0;
}

这里写图片描述

可以看到两次输出都是10。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/526170.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

初识C++之函数重载、重写、重定义的区别

在C的学习中&#xff0c;慢慢接触了一些很容易混淆的名词&#xff0c;今天就来剖析几个容易混淆的名词。 1、函数重载   重载函数是函数的一种特殊情况&#xff0c;为方便使用&#xff0c;C允许在同一范围中声明几个功能类似的同名函数&#xff0c;但是这些同名函数的形式参…

初识C++之封装

学习C不得不说到C的三大特性&#xff1a;封装、继承、多态&#xff0c;今天就先来剖析一下他的封装性。 1、什么是封装   封装就是将抽象得到的数据和行为&#xff08;或功能&#xff09;相结合&#xff0c;形成一个有机的整体&#xff0c;也就是将数据与操作数据的源代码进…

初识C++之虚函数

1、什么是虚函数   在基类中用virtual关键字修饰&#xff0c;并在一个或多个派生类中被重新定义的成员函数&#xff0c;用法格式为&#xff1a;   virtual 函数返回类型 函数名&#xff08;参数表&#xff09;    {     函数体    }    虚函数是实现多态性…

初识C++之多态

多态性是将接口与实现进行分离&#xff1b;用形象的语言来解释就是实现以共同的方法&#xff0c;但因个体差异&#xff0c;而采用不同的策略。 1、什么是多态   多态&#xff08;Polymorphism&#xff09;按字面的意思就是“多种状态”。在面向对象语言中&#xff0c;接口的…

删除一个无头单链表的非尾节点(C语言)

void DelNotTailNode(PSListNode pos) {PSListNode pNode NULL;assert(pos);if (NULL pos->pNextNode){return;}else{DataType temp 0;//交换pos和pos->pNextNode的数据&#xff08;相当于交换了两个结点的位置&#xff09;&#xff0c;使问题转换为删除pos指向的结点…

浅析Linux开发工具之gcc/g++

在windows开发平台&#xff0c;我们用惯了vc、vs等IDE&#xff08;集成开发环境&#xff09;&#xff0c;在编译好源代码之后&#xff0c;按下相应按钮&#xff0c;IDE就会为我们完成编译&#xff0c;链接的过程。然而在Linux平台下&#xff0c;却没有这么方便的开发环境&#…

Linux权限的简单剖析

一、权限是什么 权限&#xff08;privilege&#xff09;是指某个特定的用户具有特定的系统资源使用权力。举个简单的例子&#xff0c;夏日炎炎&#xff0c;你看到路边有卖西瓜的&#xff0c;你想要吃西瓜&#xff0c;你就得买它&#xff0c;买它其实就是获取你对西瓜的使用权限…

Linux文件的三种时间属性

一、Linux文件时间属性的分类 我们在用windows系统时&#xff0c;在查看磁盘文件时&#xff0c;经常会看到文件或目录的后面有一个时间信息&#xff0c;这个是文件在磁盘上别创建的时间。其实&#xff0c;在windows系统中&#xff0c;文件还有文件的修改时间、访问时间两个时间…

浅析Linux开发工具之Makefile

一、什么是Makefile 在windows平台下&#xff0c;有很多的IDE供我们使用&#xff0c;我们不会去考虑怎么把一个很大的工程编译链接为一个可执行程序&#xff0c;因为这些事IDE都为我们做了&#xff0c;而在Linux平台下&#xff0c;我们并没有这么高端的IDE供我们使用&#xff…

嵌入式面试准备

题目都摘于网上 嵌入式系统中经常要用到无限循环&#xff0c;如何用C编写死循环 while(1){}或者for(;&#x1f609; 内存分区 代码区&#xff0c;全局区&#xff08;全局变量&#xff0c;静态变量&#xff0c;以及常量&#xff09;&#xff0c;栈区&#xff0c;堆区 const关键…

C语言extern与static修饰变量

extern和static在C语言里面的作用这里就不做过多的阐述了&#xff0c;下面直接通过一个小程序来看一看他们修饰的变量的特性。 #include <stdio.h>int count 3;int main() {int i 0, count 2, sum 0;for (i 0; i < count; i 2, count){static int count 4;cou…

函数值的交换

《函数值的交换》 交换函数的几种方式&#xff1a; (1) //error int Swap1(int a,int b) { int tmp; tmp a; a b; b tmp; return 0; } 在函数Swap1中&#xff0c;a和b的地址的值并没有交换。只是把10和20赋给了a和b&#xff0c;a和b原本的值并没有改变。 (2) #…

数组的下标越界

《数组下标越界》 数组定义的一般形式为&#xff1a;类型说明符 数组名[常量表达式] [常量表达式]这两个常量表达式分别指定了二维数组的行数和列数&#xff0c;程序编译时据此向内存申请空间。 引用二维数组的格式为&#xff1a;数组名[行下标] [列下标]&#xff0c;下标都是从…

指针的加减法计算

(1) 指针&#xff1a;&#xff08;p&#xff09;表示加一个单元格&#xff08;单元格的字节随类型而定&#xff09;#include<stdio.h>int main(){int arr[10]{1,2,3,4,5,6};int *p arr;p;printf("%d %d\n",arr[0],arr[1]);return 0;}(2) 指针加数字&#xff1…

字符串在指针和数组上赋值的区别

1 #include<stdio.h> 2 int main() 3 { 4 char *str1 "abcde";// 字符串常量 5 char str2[] "abcde";// 字符数组 6 str1[0] x;//error 7 str2[0] x; 8 return 0; 9 } 注&#xff1a;代码运行到第6行崩溃 *str1是一个指…

继承与多态(一)

目录 一、继承的概念&#xff1a; 二、公有继承 三、私有继承 四、保护继承 五、保护继承与保护成员的访问 一、继承的概念&#xff1a; 在C中可以用已有的类来定义新的类&#xff0c;新类将继承原有类的全部特性&#xff0c;原有类称为基类&#xff08;父类&#xff09;&…

对象的使用

目录 一、对象指针 二、对象引用 三、对象数组 四、动态对象 五、this指针 六、组合对象 一、对象指针 定义&#xff1a;占用一块连续的内存区域&#xff0c;由此可以使用一个指向对象的指针来访问对象。它指向存放该对象的地址。 优点&#xff1a; &#xff08;1&a…

友元

概念&#xff1a;在C中&#xff0c;类具有封装性和隐蔽性&#xff0c;只有类的函数成员才能访问类的私有成员&#xff0c;程序中的其他函数是无法访问类的私有成员&#xff0c;友元为类的封装隐藏开了一个小孔&#xff0c;外界可以访问类内部的一些属性。如果某个对象说明为某个…

常对象与常成员

一、常对象 概念&#xff1a;如果某个对象不允许修改&#xff0c;则该对象称为常对象。 PS&#xff1a; &#xff08;1&#xff09;不允许常对象调用任何类的成员函数&#xff0c;而且常对象一旦定义&#xff0c;在其生存期内不允许修改&#xff0c;否则导致编译错误。 &am…

指针在数组里的加法

在C语言中&#xff0c;指针与数组经常放在一起使用&#xff0c;很多情况下数组和指针可以互相转换。 //伪代码 int arr[10] {0,1,2,3,4,5,6,7,8,9};int * p arr;arr[i] * (pi);//i是大于0小于数组长度-1的任何正整数*(arr1) arr[i]; 数组和指针的不同&#xff1a; &…