详解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 ...
随机推荐
- [DB] Kafka
介绍 一种高吞吐量的分布式发布订阅消息系统 消息类型:主体Topic(广播).队列Queue(一对一) 消息系统类型:同步消息系统.异步消息系统 常见消息产品:Redis.Kafka.JMS 术语 P ...
- [Qt] 组件
组成一个Qt应用的基本元素 窗口(window):一个部件没有嵌入其他部件中,就把这个部件叫做窗口或顶层窗口,顶层窗口没有父窗口 控件(widget):一个窗口嵌入到其他窗口中,这些窗口就叫做控件或子 ...
- git 速度慢问题解决
使用VPN上网后,如果git下载速度还是很慢说明没有正确设置代理 查看你的代理模式是否是pac代理,如果是改成全局或者Http代理,pac代理会选择性的代理网站,可能会略过某些网站. 如果还是比较慢则 ...
- Lua中的面向对象编程详解
简单说说Lua中的面向对象 Lua中的table就是一种对象,看以下一段简单的代码: 复制代码代码如下: local tb1 = {a = 1, b = 2}local tb2 = {a = 1, b ...
- MyBatis 全局配置文件详解(七)
MyBatis 配置文件作用 MyBatis配置文件包含影响 MyBatis 框架正常使用的功能设置和属性信息.它的作用好比手机里的设置图标,点击这个图标就可以帮助我们查看手机的属性信息和设置功能.其 ...
- 8.8-9 fsck、dd
8.8 fsck:检查并修复Linux文件系统 fsck命令用于检查并修复文件系统中的错误,即针对有问题的系统或磁盘进行修复,类似的命令还有e2fsck命令.有关fsck的使用需要特别注意的是: ...
- 重新整理 .net core 实践篇—————配置文件之环境配置[九]
前言 在当今在互联网微服务比较适用的情况下,docker 可以说一个利器.每次我们打包docker的时候都是适用docker 的配置文件,那么配置文件里面会设置环境变量,这个时候需要我们的应用能够识别 ...
- GPU编程和流式多处理器
GPU编程和流式多处理器 流式多处理器(SM)是运行CUDA内核的GPU的一部分.本章重点介绍SM的指令集功能. 流式多处理器(SM)是运行我们的CUDA内核的GPU的一部分.每个SM包含以下内容. ...
- JUC 并发编程--10, 阻塞队列之--LinkedBlockingDeque 工作窃取, 代码演示
直接上代码 class LinkedBlockingDequeDemo { // 循环是否结束的开关 private static volatile boolean flag1 = true; pri ...
- 阿里面试挂了,就因为面试官说我Spring 事务管理(器)不熟练?
前言 事务管理,一个被说烂的也被看烂的话题,还是八股文中的基础股之一.但除了八股文中需要熟读并背诵的那些个传播行为之外,背后的"为什么"和核心原理更为重要. 写这篇文章之前,我 ...