对C++默认构造函数的理解
在文章开始之前,首先指出对于c++新手的两个常见的误解:
一、任何class如果没有定义default constructor,就会被合成出一个来。
二、编译器合成出来的default constructor会明确设定'“class内每一个data member的默认值”。
首先我们来讨论第一个误解。编译器并不是给任何一个没有user-declared constructor的class合成出default constructor,编译器只会在需要的时候才会给没有user-declared constructor的class合成出default constructor。那到底什么时候才是需要的呢?首先我们先看一下C++ standard中的一句话:“对于class X, 如果没有任何user-declared constructor, 那么会有一个default constructor被暗中(implicitly)声明出来……一个被暗中声明出来的default constructor将是一个trivial(无能的) constructor”。对于这句话,首先解释一下,原话中的暗中声明出来并不代表编译器会给他合成出来,对于trivial constructor,编译器是不会给他们合成出来的,编译器会合成出来的只是那些nontrivial default construtor,而到底哪些才算是nontrivial default constructor呢? 《Insider C++》中给出了四种情况。
1、一个class的成员中含有带有default constructor的member class object
如果一个class中含有成员对象,而且这个对象有default constructor,, 那么编译器就会给这个class合成一个default constructor, 但是这个合成动作只有在调用需要时才会产生。也就是说,在需要时才会合成。
例如:
- class Foo{
- public Foo();
- ......
- }
- class Bar{
- Foo foo;
- char *str
- }
- void foo_bar(){
- Bar bar; //bar必须在此初始化
- if(str){}.....
- }
在上述代码中,bar必须在此初始化,当这时,编译器就会给Bar合成一个default constructor,在default constructor中安插代码调用Foo的default constructor,但是有一点,编译器为Bar合成的default constructor不会对str进行初始化,对str进行初始化,那只是程序员需要做的事情,而对于合成出的default constructor,它只满足编译器的需求,而不会去满足程序的需求。
如果class中内含一个以上的含有default constructor的object,那在为class合成的default constructor中,会按照object的声明次序调用object 的 default constructor。
2、 class继承于带有default constructor的base class
如果一个没有任何constructor的派生类继承自一个带有default constructor的base class, 那么这个派生类的default constructor被认为是nontrivial,而对于nontrivial的default constructor, 编译器会为他合成出来。在合成出的default constructor中调用base class的default constuctor.
如果设计者提供了多个constructor,但未提供default constuctor,那编译器不会合成新的default constructor,而是会扩展所有的现有的constructor,安插进去default constructor所必须的代码。如果此类中仍存在第一种情况,也就是说存在有menber object, 而且object含有default constructor, 那这些default constructor 也会被调用,在base class的default constructor被调用后。
3、 这个class中带有virtual function
无论一个class是声明(或继承)了一个virtual function, 还是派生自一个继承串联,其中有一个或多个virtual base class.不管上述哪种情况,由于缺乏由user声明的constructor, 编译器会详细记录合成一个default constructor的详细信息。
在编译期间,会做以下的扩张工作:
(1) 一个virtual function table会被编译器产生出来,内含virtual functions的地址。
(2) 编译器会合成一个vptr, 插入每一个object中。
而合成出来的default constructor,当然会为每一个object 设定vptr的初值。
4、 带有一个virtual base class的class
对于这种情况,是为了初始化虚基类指针。
以上就是对第一个误解的讨论,对于第二个误解,对于合成出的default constructor,只会做一些必要的事情,比如对base class subobject 和member class object进行初始化,而对于一些其他的nonstatic data member如整数,指针,数组等则不会进行初始化,因为那些东西对于编译器来讲并不是必要的。
总结:
以上给大家讲述了那两个误解。希望能够对大家有所帮助。只有那四种情况,编译器才会为未声明constructor的class合成出default constructor,而且被合成出来的constructor只会满足编译器的需要,而不会去满足程序的需要,而他们之所以能够完成任务(满足编译器的需要),是借着调用"member object或base class的default constructor”或是“为每一个object初始化其virtual function机制(包括vtbl的创建和vptr的正确初始化)或virtual base class机制”。而对于没有存在那四种情况并且又没有user-decleared constructor的class,我们称其拥有的是implict trivial default constructor, 而实际上,它并没有被合成出来。
所以,由此可见,以上的两个误解,都是错的。
对C++默认构造函数的理解的更多相关文章
- C++的默认构造函数与构造函数
今天看书,忽然发现自己对默认构造函数/构造函数的理解很模糊,在实际项目中写类时,这些细节问题并没有涉及到.因此,就专门对着<C++ Primer Plus>将默认构造函数/构造函数这一块简 ...
- C++ 合成默认构造函数的真相
对于C++默认构造函数,我曾经有两点误解: 类如果没有定义任何的构造函数,那么编译器(一定会!)将为类定义一个合成的默认构造函数. 合成默认构造函数会初始化类中所有的数据成员. 第一个误解来自于我学习 ...
- 转 关于C#中派生类调用基类构造函数的理解
关于C#中派生类调用基类构造函数的理解 .c#class 本文中的默认构造函数是指在没有编写构造函数的情况下系统默认的无参构造函数 1. 当基类中没有自己编写构造函数时,派生类默认的调用 ...
- C++中关于重载默认构造函数与默认全部参数的构造函数的使用注意
# include<iostream>using namespace std;class Time{public: //公用成员函数 ...
- C++对象模型的那些事儿之三:默认构造函数
前言 继前两篇总结了C++对象模型及其内存布局后,我们继续来探索一下C++对象的默认构造函数.对于C++的初学者来说,有如下两个误解: 任何class如果没有定义default constructor ...
- C++对象模型——默认构造函数的合成
最近在学习C++对象模型,看的书是侯捷老师的<深度探索C++对象模型>,发现自己以前对构造函数存在很多误解,作此笔记记录. 默认构造函数的误解 1.当程序猿定义了默认构造函数,编译器就会直 ...
- 【C++】默认构造函数
参考文献: 1.黄邦勇帅 2.http://www.cnblogs.com/graphics/archive/2012/10/02/2710340.html 3.http://blog.csdn.ne ...
- C++ 没有合适的默认构造函数(无参数构造函数)
本来今天吧,想写一个proxy class的范例,写着写着出了个问题,见如下代码 ; Array1D* _elemArray = new Array1D[_cap]; 同时我为Array1D这个类写了 ...
- C/C++ 关于默认构造函数
前言: 在C++中,对于一个类,C++的编译器都会为这个类提供四个默认函数,分别是: A() //默认构造函数 ~A() //默认析构函数 A(const A&) //默认拷贝构造函数 A&a ...
随机推荐
- NuGet学习笔记(1)——初识NuGet及快速安装使用(转)
关于NuGet园子里已经有不少介绍及使用经验,本文仅作为自己研究学习NuGet一个记录. 初次认识NuGet是在去年把项目升级为MVC3的时候,当时看到工具菜单多一项Library Package M ...
- struts2获取request、session、application
struts2获取request.session.application public class LoginAction extends ActionSupport implements Reque ...
- HTML5 学习
1.<header> 标签定义文档的页眉(介绍信息) 标签是 HTML 5 中的新标签 <header> <h1>Welcome to my homepage< ...
- ubuntu 下源码安装Postgreql pgAdmin3
一.安装 PostgreSQL 1.安装相关依赖,在终端下执行: sudo apt-get install zlib1g-dev sudo apt-get install libreadline ...
- PHP String
PHP 5 String 函数 PHP String 函数是 PHP 核心的组成部分.无需安装即可使用这些函数. 函数 描述 addcslashes() 返回在指定的字符前添加反斜杠的字符串. add ...
- PHP 初学者的学习线路和建议【1】
先来看下PHP初学者的学习线路: (1) 熟悉HTML/CSS/JS等网页基本元素,完成阶段可自行制作简单的网页,对元素属性相对熟悉. (2) 理解动态语言的概念和运做机制,熟悉基本的PHP语法. ( ...
- python MySQLdb、socket与进线程
1 centos下 安装MySQLdb模块 a 首先需要先安装 setuptool b yum install -y mysql_devel 头文件 c yum install -y python_d ...
- Redis同步(主从复制)
目录1.Replication的工作原理2.如何配置Redis主从复制3.应用示例 1.Replication的工作原理在Slave启动并连接到Master之后,它将主动发送一条SYNC命令.此后Ma ...
- 图片输出onerror事件
<img src=".<?php echo $img[0];?>" onerror="this.src='img/zanwu.jpg'" st ...
- Linux系统挂载点与分区的关系(转载)
计算机中存放信息的主要的存储设备就是硬盘,但是硬盘不能直接使用,必须对硬盘进行分割,分割成的一块一块的硬盘区域就是磁盘分区.在传统的磁盘管理中,将一个硬盘分为两大类分区:主分区和扩展分区.主分区是能够 ...