[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. WCF连接被意外关闭

    WCF项目中出现常见错误的解决方法:基础连接已经关闭: 连接被意外关闭 在我们开发WCF项目的时候,常常会碰到一些莫名其妙的错误,有时候如果根据它的错误提示信息,一般很难定位到具体的问题所在,而由于W ...

  2. Parameter Binding in ASP.NET Web API(参数绑定)

    Parameter Binding in ASP.NET Web API(参数绑定) 导航 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnbl ...

  3. Xcode开发和调试总结

    Xcode是iOS开发主要的工具.IDE.关于Xcode的细枝末节,可以参考苹果的官方文档或者众多的说明.此文档主要涉及常用开发和调试注意事项,参考版本为Xcode 5.1.1. 目标设置: 在此,我 ...

  4. Windows Serer 2003 配置手册 – 创建Active Dictionary域

    域与工作组的关系 实际上我们可以把域和工作组联系起来理解,在工作组上你一切的设置在本机上进行包括各种策略,用户登录也是登录在本机的,密码是放在本机的数据库来验证的.而如果你的计算机加入域的话,各种策略 ...

  5. 企业架构研究总结(44)——企业架构与建模之Archimate视图和视角

    3. ArchiMate的视角与视图 创建.维护一个企业架构是一件非常复杂繁琐的事情,因为这项工作需要面对许多背景.利益各异的干系人,对他们所关注的问题进行解答,并能够在他们之间形成无障碍的沟通流.为 ...

  6. IOS7学习之路二(处理ios6到ios7后UITableView的两个显示问题)

    1.在ios6开发的项目,当用ios7的虚拟机显示的时候会出现UINavigationItem遮挡TableView的问题: 下面是对比显示效果: 我的处理方法是: 在UITableViewContr ...

  7. 线性回归,logistic回归和一般回归

    1 摘要 本报告是在学习斯坦福大学机器学习课程前四节加上配套的讲义后的总结与认识.前四节主要讲述了回归问题,回归属于有监督学习中的一种方法.该方法的核心思想是从连续型统计数据中得到数学模型,然后将该数 ...

  8. tomcat创建一个windows服务

    具体步骤如下: 1.把JDK解压到C:\Program Files\Java下,Tomcat解压到D:\tomcat下 2.配置环境变量 JAVA_HOME:C:\Program Files\Java ...

  9. VS XCOPY

    有时在项目开发过程中,某个项目不直接依赖于另外一个项目,也就是说在编码上不需要使用另外一个项目上的代码,但实际运行的时候需要用到,我们通常的做法是把另外项目中需要用到的DLL拷过来放到该项目的BIN文 ...

  10. Winform 让跨线程访问变得更简单

    Winform 让跨线程访问变得更简单 前言 由于多线程可能导致对控件访问的不一致,导致出现问题.C#中默认是要线程安全的,即在访问控件时需要首先判断是否跨线程,如果是跨线程的直接访问,在运行时会抛出 ...