C++对象的构造

原创 2016年05月31日 23:39:26

C++中通过用户自定义类建立对象时,需要调用构造函数,这里包含默认构造函数、复制构造函数和自定义构造函数。其中自定义构造函数,按照函数重载机制进行匹配调用,与普通重载函数调用类似。因此,这里讨论的是默认构造函数和复制构造函数,因为这两种构造函数如果用户不显式定义,会在特定情况下被编译器合成出来。编译器合成的规则并不明显,C++标准做的说明只是“在需要的时候合成”。

默认构造函数(default constructor)

默认构造函数就是不需要参数的构造函数或者参数都带有默认值的构造函数。
理解默认构造函数需要从程序语义层面和编译器层面(C++实现)两个方面来入手。考虑如下代码:

class A{
    int a;
    float b;
};

A objA;
...

从程序语义层面上看,变量objA需要一个默认构造函数,并且最好能初始化成员变量的值为0,但是由于没有明确定义默认构造函数,因此编译器会是否合成出这个默认构造函数取决于编译器层面上能否进行编译,因此就衍生出了trivial和non-trivial默认构造函数的概念,如果是一个non-trivial性质的,编译器就会合成;否则就不会合成。
编译器总共支持四种non-trivial的情形,出现这些情形中的任何一个或多个都会当做non-trivial从而合成出默认构造函数:
1. 类的成员对象存在默认构造函数
2. 类的基类存在默认构造函数
3. 存在虚函数的类
4. 存在虚基类的类

类的成员对象含有默认构造函数

编译器合成的版本的唯一功能就是调用所有这些对象的默认构造函数,这就是编译器必须要满足的要求,其他的初始化操作都不会施行。

class A{
public: A():val(0){}
private: int val;
};
class D : A{
    A a;
    int dval;
};

D objD;

对象 objD 会被调用编译器合成的构造函数,用来唯一功能就是调用成员对象a的默认构造函数。对于用户定义的其他构造函数,如果没有初始化相应的带有默认构造函数的成员对象,编译器也会合成相应的初始化调用语句插入到用户定义的构造函数内最开始处。如果存在多个需要初始化的成员对象,那么会按照类中成员对象声明的顺序来依次调用每个成员的构造函数。

类的基类存在默认构造函数

如果基类存在用户定义的默认构造函数,而子类没有定义默认构造函数时,编译器会合成non-trivial的默认构造函数。对于多个基类的继承,会按照声明顺序依次调用父类的默认构造函数。如果存在用户定义的构造函数而没有默认构造函数,那么会被编译器插入到所有这些构造函数最开始初。另外,如果同时存在有默认构造函数的成员对象,会在调用完父类的构造函数之后,再调用成员对象的构造函数。

存在虚函数的类

对于存在多态性质的类,根据C++多态的实现方式,编译器会为每个对象插入一个虚函数表指针(vptr),因此默认构造函数必须进行这个vptr成员的初始化,放置虚函数表的地址,此时non-trivial默认构造函数必须被合成,插入相应代码来完成这些工作。

class B{
public:
    virtual void fun();
    ...
};
class A:B{
public:
    virtual void fun();
    ...
};

B * pb = new A;
pb->fun();

上述用new创建类A的对象时会调用默认构造函数,由于多态,默认构造函数被编译器合成后初始化虚函数表指针vptr,最终的调用操作会被解析到:

(pb->vptr[1])(pb); //假设虚函数fun为声明的第一个虚函数,地址在在虚函数表中第二项

存在虚基类的类

与前面多态性质的类对象存在vptr类似,虚基类的支持虽然不同编译器有差异,但是共通点都是需要解决虚基类在派生类中的位置能在执行期明确,为了实现这个需求,要么继续使用vptr,要么使用虚基类指针vbc,总之都需要编译器为每个对象合成出新的指针成员,这样就和前面多态的情况类似,需要构造默认构造函数,安插对合成的指针成员的初始化操作。

除了上述四种情况外,其他情况下,如果用户没有定义默认构造函数,编译器并不会合成,因此
1. 不是所有默认构造函数没有定义的类编译器都会合成
2. 编译器合成的默认构造函数不会初始化所有数据成员

复制构造函数(copy constructor)

复制构造函数的调用有三个地方
1. 显式调用
2. 非引用的传递参数
3. 非引用的函数返回

最基本的复制构造就是基于bitwise语义,也就是基本变量的赋值操作。编译器是否合成复制构造函数也是看trivial和non-trivial,而这取决于类是否表现出bitwise拷贝语义,与前面的默认构造函数类似,存在以下四种情况不表现出bitwise拷贝语义,从而需要合成:
1. 成员对象存在复制构造函数
2. 基类存在复制构造函数
3. 多态性的虚函数
4. 带有虚基类

可以很明显看出,这四种与前面默认构造函数的non-trivial完全相同。

C++中的构造函数与创建对象的(简单)过程

•构造函数      –和类名相同      –没有返回值类型      –只在建立对象的时候被自动调用一次      –调用构造函数的主要目的是初始化对象 •一个对象的创建过程:      –...
  • love9099
  • love9099
  • 2015年01月19日 15:22
  • 2403

C++对象的构造函数和析构函数详解

 C++对象的构造函数和析构函数详解 你好,C++(33)对象生死两茫茫 6.2.3 一个对象的生与死:构造函数和析构函数 6.2.2  使用类创建对象 完成某个类的声明并且定义其成员函...
  • mynote
  • mynote
  • 2015年03月18日 12:23
  • 1090

理解C++存在继承和组合的对象构造函数调用顺序

前言: Hello,全世界早上好啊,哈哈,9月你好,2015年9月的第一天,也是自己本科生涯的最后一年了,接下来就是校招走上自己的职场,昨天是大四的第一节课,上的是软件工程,挺喜欢的一门课,...
  • u013777351
  • u013777351
  • 2015年09月01日 08:52
  • 2336

技巧:在 C-C++中如何构造通用的对象链表

  • 2009年04月08日 19:12
  • 272KB
  • 下载

计算机程序设计(C++)第10周编程作业数据的抽象和封装——类(2)——构造函数、析构函数和指向对象的指针

/* 1定义一个带重载构造函数的日期类 题目内容: 定义一个带重载构造函数的日期类Date,数据成员有年、月、日;成员函数包括:一个带参数的构造函数Date(int,int,int),一个不带参数的构...
  • guaxie9392
  • guaxie9392
  • 2017年05月12日 14:37
  • 593

C++ 对象构造, 拷贝, 赋值和隐式类型转换总结

C++ 中对象创建(构造)、拷贝、赋值、隐式类型转换的阶段性总结 关心效率和拷贝开销,写在代码注释中 关键字:对象创建、拷贝构造、赋值、隐式类型转换、explicit、按值传递 (pass...
  • Andeewu
  • Andeewu
  • 2013年06月05日 09:14
  • 884

C++13.1 复制构造函数-----临时对象(不是很懂)

简介 =============================================================================================== ...
  • hlsdbd1990
  • hlsdbd1990
  • 2015年06月24日 15:04
  • 408

C++对象模型之第二章——构造函数语义学

一、构造函数 编译器会合成4个隐性的非无用的构造函数的情况: 1.含有带有默认构造函数的成员类的对象。 这个合成构造函数只有被真正使用时,才会被合成。这时有一个问题产生,即在多个不同的编译 块产生都各...
  • chenyixin121738
  • chenyixin121738
  • 2017年05月04日 22:34
  • 327

c++对象模型研究5:构造、解构、拷贝

纯虚函数 虚函数是为了重载和多态的需要,子类中可以重写或不重写该函数;纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像Java中的interface。 无继承情况下的对象构造 参考:...
  • losophy
  • losophy
  • 2014年03月05日 17:06
  • 1212

一个有关C++中对象构造、析构和虚函数的问题

问题:执行如下C++代码,程序的输出是什么? #include using namespace std; class Base{ public: Base(){ cout ...
  • spiralgalaxy
  • spiralgalaxy
  • 2012年10月15日 17:10
  • 194
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++对象的构造
举报原因:
原因补充:

(最多只允许输入30个字)