[1]中提到,规范的派生类构造函数三个要点:

  1. 首先创建基类对象
  2. 应通过成员初始化列表,创建基类对象
  3. 应该初始化本派生类新增的成员变量

那在构造派生类实例的过程中,其基类(以及多继承的时候多个基类)/当前对象属性/当前对象的构造顺序如何呢?

下面初步分析:

1 不显式调用基类构造函数

C继承B1和B2

#include<iostream>
using namespace std;
class B1
{
public:
B1(){ cout<<"B1"<<endl;}
};
class B2
{
public:
B2(){cout<<"B2"<<endl;}
};
class C:public B1,public B2
{
public:
C(){cout<<"C"<<endl;}
};
int main()
{
C obj;
return 0;
}
/*output
B1
B2
C
*/

其中,冒号指出C的基类是B1和B2,public限定符指出B1和B2都是公有基类,我们称之为公有派生[1]。

公有派生中,基类的公有成员(成员变量、成员方法)变成了派生类的公有成员。基类的私有成员变成了派生类的私有成员,但是不能直接访问,只能借助基类的public和protected方法访问。

这时,程序将使用基类默认构造函数。

也就是说C的写法相当于在成员初始化列表调用基类的默认构造函数:C():B1(),B2(){cout<<"C"<<endl;}

  • B1和B2的构造顺序只和继承的顺序有关,和成员初始化列表中的顺序无关

调整继承顺序class C:public B2,public B1,输出顺序为B2 B1.而修改成员初始化列表中的顺序没有效果。

2 显式调用基类构造函数

同样的,B1和B2的构造顺序只和继承的顺序有关,和成员初始化列表中的顺序无关

class B1
{
public:
B1(){ cout<<"B1"<<endl;}
B1(int i){ cout<<"B1:"<<i<<endl;}
};
class B2
{
public:
B2(){cout<<"B2"<<endl;}
B2(int i){ cout<<"B2:"<<i<<endl;}
};
class C:public B2,public B1
{
public:
C():B1(10),B2(20){cout<<"C"<<endl;}
};
int main()
{
C obj;
return 0;
}
/*output
B2:20
B1:10
C
*/

更进一步可以把C中构造函数参数作为实参传递给基类构造函数:

C(int x,int y):B1(x),B2(y){cout<<"C"<<endl;}

3 封闭类

有成员对象的类称为封闭类,这是对象组合的一种实现方式[2]。

修改C为:

class C:public B2,public B1
{
private:
int x;
B1 memberb1;
B2 memberb2;
public:
C():B1(10),B2(20){cout<<"C"<<endl;}
};
/*output
B2:20
B1:10
B1
B2
C
*/
  • 构造子类实例过程中,依次进行如下构造:

    • 构造基类
    • 构造当前派生类的成员对象
    • 构造当前派生类(执行自己的构造函数)

3.1 对当前对象属性使用成员初始化列表语法

当前对象属性就是成员对象

class C:public B2,public B1
{
private:
int x;
B1 memberb1;
B2 memberb2;
public:
C():B1(10),B2(20),memberb1(1),memberb2(2){cout<<"C"<<endl;}
};
/*output
B2:20
B1:10
B1:1
B2:2
C
*/

和基类很相似。

在成员初始化列表,如果不显式调用成员对象的构造函数,程序就会调用默认构造函数。显式调用,程序就会调用你指定的构造函数。

成员对象之间的构造顺序之和声明顺序有关,和在Member Initialization List中的顺序无关。

当然,也可以不指定初始化列表,在构造函数中再进行成员对象的赋值,这会导致成员对象被构造多次。 更重要的是,因为常量类型、引用类型的成员不接受赋值,它们只能在初始化列表中进行初始化。[2]

到这里,基类/当前对象属性/当前对象的构造顺序终于搞清楚了。

Reference

C++类继承中,基类/当前对象属性/当前对象的构造顺序的更多相关文章

  1. C++//菱形继承 //俩个派生类继承同一个基类 //又有某个类同时继承俩个派生类 //成为 菱形继承 或者 钻石 继承//+解决

    1 //菱形继承 2 //俩个派生类继承同一个基类 3 //又有某个类同时继承俩个派生类 4 //成为 菱形继承 或者 钻石 继承 5 6 #include <iostream> 7 #i ...

  2. 修改tt模板让ADO.NET C# POCO Entity Generator With WCF Support 生成的实体类继承自定义基类

    折腾几天记载一下,由于项目实际需要,从edmx生成的实体类能自动继承自定义的基类,这个基类不是从edmx文件中添加的Entityobject. 利用ADO.NET C# POCO Entity Gen ...

  3. lua中基类和“继承机制”

    基类:基类定义了所有对于派生类来说普通的属性和方法,派生类从基类继承所需的属性和方法,且在派生类中增加新的属性和方法. 继承:继承是C++语言的一种重要机制,它允许在已定义的类的基础上产生新类. lu ...

  4. 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)

    [源码下载] 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成 ...

  5. C# 类型运算符重载在类继承中的调用测试

    这是一篇晦涩难懂的片面的研究 一,简单的继承层次 class CA { } class CB : CA{ } class CC : CB{ } } void Test(CA oa){//CATest ...

  6. C++类继承中的构造函数和析构函数 调用顺序

    思想: 在C++的类继承中,构造函数不能被继承(C11中可以被继承,但仅仅是写起来方便,不是真正的继承) 建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,依次类推: 析构对象时, ...

  7. 【Android进阶】为什么要创建Activity基类以及Activity基类中一般有哪些方法

    现在也算是刚刚基本完成了自己的第一个商业项目,在开发的过程中,参考了不少人的代码风格,然而随着工作经验的积累,终于开始慢慢的了解到抽象思想在面向对象编程中的重要性,这一篇简单的介绍一下我的一点收获. ...

  8. C++中的类继承(2)派生类的默认成员函数

    在继承关系里面, 在派生类中如果没有显示定义这六个成员 函数, 编译系统则会默认合成这六个默认的成员函数. 构造函数. 调用关系先看一段代码: class Base { public : Base() ...

  9. C++基础——类继承中方法重载

    一.前言 在上一篇C++基础博文中讨论了C++最基本的代码重用特性——类继承,派生类可以在继承基类元素的同时,添加新的成员和方法.但是没有考虑一种情况:派生类继承下来的方法的实现细节并不一定适合派生类 ...

随机推荐

  1. Linq to sql与EF零碎知识点总结

    ------------------------------第一天(2013-3-25) 1.ado.net实体模型,(Ef) 2.创建上下文对象: 调用相应方法,最后调用.savechanges() ...

  2. Visual Studio 编辑器

    如何扩展 Visual Studio 编辑器 在 Visual Studio 2010 的时代,扩展 Visual Studio 的途径有很多,开发者可以选择宏.Add-in.MEF 和 VSPack ...

  3. vector cin

    vector<int>vec1,vec2; int ival; cout<<"Ender numbers for vector1(-1 to end):"& ...

  4. Hadoop将本地文件复制到Hadoop文件系统

    代码: package com.hadoop; import java.io.BufferedInputStream; import java.io.FileInputStream; import j ...

  5. 职责链(Chain of Responsibility)模式

    一. 职责链(Chainof Responsibility)模式 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的 ...

  6. UNIX基础知识--<<UNIX 环境编程>>读书笔记

    1 shell程序就是位于应用软件与系统调用之间的程序   每个用户登录系统,系统就会为用户分配shell (用户的登录的口令文件 在  /etc/passwd 2 ls filename  运行原理 ...

  7. 初探performance.timing API

    初探performance.timing API   浏览器新提供的performance接口精确的告诉我们当访问一个网站页面时当前网页每个处理阶段的精确时间(timestamp),以方便我们进行前端 ...

  8. [转]iOS hacking resource collection

    Link:http://www.securitylearn.net/tag/apple-ios-hacking-slides/ A collection of iOS research present ...

  9. C#:.net/方法/字符串/数组

    C#:.net/方法/字符串/数组,那点事 首先还是先说下(几个概念的东西)c#下的.net平台的构造快及其功能作用和程序集: .net: .net平台是由:a:运行库+b:全面基础类库(这个是从程序 ...

  10. 分析Sizzle引擎

    jQuery 2.0.3 源码分析Sizzle引擎 - 打造高效查询 为什么Sizzle很高效? 首先,从处理流程上理解,它总是先使用最高效的原生方法来做处理 HTML文档一共有这么四个API: ge ...