详解C++中继承的基本内容
有些类与类之间存在特殊的关系,有共性也有特性,比如动物类可以细分为猫,狗等。下级别的成员除了拥有上一级的共性,还有自己的特性,这个时候就可以考虑继承的技术,减少重复代码。
一、继承中的对象模型
1.1 子类继承父类中的成员变量
子类从父类继承的成员变量,是属于子类呢还是属于父类呢?我们定义如下示例:
class father{
public:
int f_a;
protected:
int f_b;
private:
int f_c;
};
class son : public father{ // 从son是father的子类
public:
int s_a;
};
son S1;
cout << "son所占的内存为:" << sizeof(S1) << endl;
输出结果为:
son所占的内存为:16
因此可以看出,父类中的所有变量都被子类给继承了下来,都属于子类的一部分。虽然父类中 private 访问权限的成员不能被子类访问,但是仍然属于子类的一部分。同理,在子类继承父类时,除了继承父类中所有的成员变量,也同时继承了除了父类构造函数外的所有成员函数,这样便可以有效节省代码量,提高代码复用效率。至于子类与父类构造函数之间的关系,将在后文进行解释。
1.2 子类与父类构造函数的原则
上面提到,子类可以继承父类中所有的成员变量和成员方法,但不继承父类的构造函数。因此在创建子类对象时,为了初始化从父类继承来的成员变量,系统需要调用父类的构造方法。我们都知道,任何一个类都要有构造函数,那么子类的构造函数和父类的构造函数之间的关系是怎样的?
如果子类没有定义构造函数,则调用父类的无参构造函数;
如果子类定义了构造函数(不管是无参还是有参),在创建子类对象时首先执行父类的无参构造函数,然后执行自己的构造函数;
这两种情况下,不管子类是否定义了新的构造函数,只要没有显示的调用父类中的构造函数,都只会调用父类中的无参构造函数。
如果父类中自己定义了有参构造函数,那么编译器便不会再提供默认无参构造函数,子类便只能在构造函数中显示的调用父类的构造函数来对父类成员进行初始化。
仅仅通过文字会比较抽象,下面我们通过几个具体的例子来解释一下上面的构造原则:
class father{
public:
string f_a;
// 与默认构造函数一样,都是无参构造函数
father(){
cout << "我是父类的无参构造函数" << endl;
}
// 自定义的有参构造函数
father(string f_a){
this->f_a = f_a;
cout << "我是父类的有参构造函数" << endl;
}
};
class son : public father{
public:
string f_a;
// 子类中的构造函数,在没有显示调用父类构造函数的情况下,默认调用父类中的无参构造函数
son(string f_a){
cout << "我是子类的构造函数" << endl;
}
};
son S1("abc"); // 如果此时创建对象,那么将会调用父类中的无参构造函数,然后调用子类的构造函数
输出结果如下:
我是父类的无参构造函数 //调用父类的无参构造函数
我是子类的构造函数 // 调用子类的构造函数
假如父类中只定义了有参构造函数,在创建子类对象时编译器便找不到父类中的无参构造函数,于是便会报错。解决这类问题的办法就是在子类构造函数中显示地调用父类的有参构造函数来对父类的成员初始化。
class father{
public:
string f_a;
// 自定义的有参构造函数
father(string f_a){
this->f_a = f_a;
cout << "我是父类的有参构造函数" << endl;
}
};
class son : public father{
public:
string f_a;
// 子类中的构造函数,由于父类中没有提供无参构造函数,导致出错
//son(string f_a){
// cout << "我是子类的构造函数" << endl;
//}
son(string f_a):father("father"){ // 显示调用父类中的有参构造函数
cout << "我是子类的构造函数" << endl;
}
};
son S1("abc"); // 如果此时创建对象,那么将会调用父类中的无参构造函数,然后调用子类的构造函数
输出结果如下:
我是父类的有参构造函数 //显示调用父类的有参构造函数
我是子类的构造函数 // 调用子类的构造函数
二、继承中的构造和析构顺序
在上面一节中也简单提到了继承过程中的构造顺序,原则是:
- 父类先执行构造函数,子类然后执行构造函数
- 子类先执行析构函数,父类然后执行析构函数
下面通过一个简单的例子来简要说明一下:
class father{
public:
string f_a;
father(){
cout << "我是父类的构造函数" << endl;
}
~father(){
cout << "我是父类的析构函数" << endl;
}
};
class son : public father{
public:
string f_a;
son(){
cout << "我是子类的构造函数" << endl;
}
~son(){
cout << "我是子类的析构函数" << endl;
}
};
输出结果如下:
我是父类的构造函数
我是子类的构造函数
我是子类的析构函数
我是父类的析构函数
二、同名成员的处理
当子类与父类中出现同名的成员,如何通过子类对象访问到子类或者父类中的同名数据呢?首先我们先理解一下为什么子类和父类中会出现同名的成员变量和成员函数。这是因为变量和函数都有它的作用域,在同一个作用域中不能出现两个重名的变量,但是在不同的作用域中可以出现重名。这就相当于每个学校都有一个校长,但是在同一所学校内只能有一个校长。由于父类和子类是两个不同的作用域,所以可以出现重名的变量或者函数。
class father{
public:
string f_a;
int f_b;
father(){ // 父类中的无参构造函数,初始化父类成员
f_a = "father";
f_b = 10;
}
void show(){
cout << " 我是father!" << endl;
}
};
class son : public father{
public:
string f_a;
son(){ // 子类中的无参构造函数,初始化子类成员
f_a = "son";
}
void show(){
cout << " 我是son!" << endl;
}
};
son S1;
cout << "f_b = " << S1.f_b << endl; // 子类对象可以像访问自己的成员一样访问从父类继承下来的成员
cout << "f_a = " << S1.f_a << endl; // 但是,从父类继承了一个变量 f_a , 子类自己也有一个 f_a , 那么访问的是哪个呢?
输出结果为:
f_b = 10
f_a = son // 访问的是子类中的成员
通过上述实例可以看出,当子类和父类中有同名的成员时,子类对象优先访问子类中的成员,若想访问父类中的成员,应该指定父类成员的作用域:
cout << "f_a = " << S1.father::f_a << endl; // 给 f_a 加上一个父类的作用域
输出结果为:
f_a = father // 访问到了父类中的同名成员
对成员函数的访问也是一样,比如:
S1.show(); // 访问子类中的show
S1.father::show(); // 访问父类中的show
输出结果为:
我是son!
我是father!
详解C++中继承的基本内容的更多相关文章
- 详解C++中的多态和虚函数
一.将子类赋值给父类 在C++中经常会出现数据类型的转换,比如 int-float等,这种转换的前提是编译器知道如何对数据进行取舍.类其实也是一种数据类型,也可以发生数据转换,但是这种转换只有在 子类 ...
- Python 在子类中调用父类方法详解(单继承、多层继承、多重继承)
Python 在子类中调用父类方法详解(单继承.多层继承.多重继承) by:授客 QQ:1033553122 测试环境: win7 64位 Python版本:Python 3.3.5 代码实践 ...
- 详解Javascript的继承实现(二)
上文<详解Javascript的继承实现>介绍了一个通用的继承库,基于该库,可以快速构建带继承关系和静态成员的javascript类,好使用也好理解,额外的好处是,如果所有类都用这种库来构 ...
- (转载)详解Javascript中prototype属性(推荐)
在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不存在类(Class)的概念的,javascript中不 ...
- 详解Springboot中自定义SpringMVC配置
详解Springboot中自定义SpringMVC配置 WebMvcConfigurer接口 这个接口可以自定义拦截器,例如跨域设置.类型转化器等等.可以说此接口为开发者提前想到了很多拦截层面的需 ...
- jQuery:详解jQuery中的事件(二)
上一篇讲到jQuery中的事件,深入学习了加载DOM和事件绑定的相关知识,这篇主要深入讨论jQuery事件中的合成事件.事件冒泡和事件移除等内容. 接上篇jQuery:详解jQuery中的事件(一) ...
- 图文详解Unity3D中Material的Tiling和Offset是怎么回事
图文详解Unity3D中Material的Tiling和Offset是怎么回事 Tiling和Offset概述 Tiling表示UV坐标的缩放倍数,Offset表示UV坐标的起始位置. 这样说当然是隔 ...
- 【转】详解C#中的反射
原帖链接点这里:详解C#中的反射 反射(Reflection) 2008年01月02日 星期三 11:21 两个现实中的例子: 1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内 ...
- 详解Webwork中Action 调用的方法
详解Webwork中Action 调用的方法 从三方面介绍webwork action调用相关知识: 1.Webwork 获取和包装 web 参数 2.这部分框架类关系 3.DefaultAction ...
随机推荐
- java基础——简易计算器的实现
计算器: import java.util.Scanner;public class CalculateDemo { public static void main(String[] args ...
- 027. Python面向对象的__init__方法
__init__魔术方法(构造方法) 触发时机:实例化对象,初始化的时候触发 功能:为对象添加成员 参数:参数不固定,至少一个self参数 返回值:无 基本用法,至少含有一个参数 class MyCl ...
- Linux进阶之find命令、xshell速度慢的解决和Linux警告音的关闭
一.Linux警告音关闭方法 1. 修改/etc/inputrc配置文件 set bell-style none #取消该行注释 2. 修改~/.bashrc配置文件 在后面增加: setter ...
- systemverilog数组类型
- 灵动微电子ARM Cortex M0 MM32F0010 UART1和UART2中断接收数据
灵动微电子ARM Cortex M0 MM32F0010 UART1和UART2中断接收数据 目录: 1.MM32F0010UART简介 2.MM32F0010UART特性 3.MM32F0010使用 ...
- 解决element-ui el-input输入框内容无法修改的问题
wqy的笔记:http://www.upwqy.com/details/271.html el-input 中 使用 :value 时 input输入的内容无法修改 <el-input : ...
- Kali Linux 2021.2 发布 (Kaboxer, Kali-Tweaks, Bleeding-Edge & Privileged Ports)
Kali Linux 简介 Kali Linux 是基于 Debian 的 Linux 发行版,旨在进行高级渗透测试和安全审核.Kali Linux 包含数百种工具,可用于各种信息安全任务,例如渗透测 ...
- 如何屏蔽 iOS 软件自动更新,去除更新通知和标记
适用于 iOS.iPadOS 和 watchOS,即 iPhone.iPad 和 Apple Watch 通用. 请访问原文链接:https://sysin.org/article/Disable-i ...
- SpringBoot基础学习(三) 自定义配置、随机数设置及参数间引用
自定义配置 SpringBoot免除了项目中大部分手动配置,可以说,几乎所有的配置都可以写在全局配置文件application.peroperties中,SpringBoot会自动加载全局配置文件从而 ...
- 最适合新手的Redis Cluster搭建过程
好记性不如烂笔头,记录分片高可用Redis Cluster的搭建过程 Redis集群演进过程 Redis单节点 主从复制: 复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复. 故 ...