详解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 ...
随机推荐
- [bug] Unable to create initial connections of pool.
原因1 pom中mysql依赖的版本不对,导致无法连接mysql 原因2 SSL设置问题 参考 https://blog.csdn.net/qq_26346457/article/details/79 ...
- [c++] 子类构造函数中有默认参数
子类创建对象时调用父类的构造函数: 1 #include <iostream> 2 using namespace std; 3 class Base 4 { 5 public: 6 Ba ...
- 1.3Linux 终端命令格式
Linux 终端命令格式 目标 了解终端命令格式 知道如何查阅终端命令帮助信息 01. 终端命令格式 bashcommand [-options] [parameter] 说明: command:命令 ...
- workbench msvcr120.dll 文件缺失问题
开始以为是dll文件缺失,后来下载了好几个版本的替换到system32文件夹下.依然不起作用. 后来看到解决办法了,原文在此:http://forums.mysql.com/read.php?169, ...
- Spring 是什么?
概述 Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好.易于测试.可重用的代码. Spring 框架是一个开源的 Ja ...
- php反转字符串的三种方法
(假设有字符串abcd,用php实现字符串翻转) 1.第一种php有自带的函数strrev可以轻松实现: $str = 'abcd'; //第一种自带strrev实现翻转 echo strrev($s ...
- Ubuntu中的MySQL修改root密码的多种方法
查看.修改mysql的用户名和密码第一步:这时你需要进入/etc/mysql目录下,然后sudo vim/vi debian.cnf查看里面的用户名和密码,然后使用这个文件中的用户名和密码进入mysq ...
- VirtualBox安装配置CentOS7(含网络连接配置)
最近需要用到CentOS7,特地在虚拟机上安装一遍,中间走了很多弯路,特地在此处进行记录 前置条件: 1.本地完成Oracle VM VirtualBox,我安装的是6.1版本 2.下载CentOS安 ...
- Redis持久化锦囊在手,再也不会担心数据丢失了
大家好,我是小羽. Redis 的读写都是在内存中进行的,所以它的性能高.而当我们的服务器断开或者重启的时候,数据就会消失,那么我们该怎么解决这个问题呢? 其实 Redis 已经为我们提供了一种持久化 ...
- GO学习-(37) 使用Air实现Go程序实时热重载
使用Air实现Go程序实时热重载 今天我们要介绍一个神器--Air能够实时监听项目的代码文件,在代码发生变更之后自动重新编译并执行,大大提高gin框架项目的开发效率. 为什么需要实时加载? 之前使用P ...