构造函数是C++的一个很基础的知识点,在平时编程的时候,相信大家都很熟悉,虽然比较基础,但是细究下来,还是有不少细节需要注意。这篇文章主要总结C++构造函数需要注意一些细节,一方面,可以帮助下大家巩固下这方面知识。同时,也是有助于自己更好得整理以前的知识。
  
让我们由一个对象的创建开始。当一个对象创建的时候,编译器就会调用这个对象的构造函数,在这个时候,或许大家就会有疑问了:我并没有为对象指定构造函数,那么编译器调用的构造函数由哪里来呢?

有这点疑惑就是一个好的开始,那么当我们没有指定构造函数时,编译器调用的构造函数由哪里来呢?,答案是编译器会自己为对象产生所需的构造函数。

那么现在又有了两个问题:
>1.编译器在什么条件下会为我们自动生成默认构造函数?
>
>2.自动生成的构造函数主要做了什么?

我们先来回答问题1,答案是:
>在我们没有对象指定构造函数的时候,编译器会为我们生成默认构造函数,拷贝构造函数,默认析构函数。

这样的话,无论我们使用通过new,拷贝来构造一个对象就都可以完成了。在此需要提及一下拷贝构造函数和赋值构造函数的区别,请看下例:

class Obj{}; // 声明一个对象Obj
Obj a; //调用默认构造函数来构造对象
Obj b(a);//调用默认拷贝构造函数来构造对象
Obj c = b;//调用的也是拷贝构造函数,最好将其写做 Obj c(b)。

所以,当我们不需要编译器生成的构造函数时,就应该明确说出来,即如果们声明自己的构造函数,拷贝构造函数的话,编译器就不会为我们生成这些函数了。
通过利用这点我们就可以限制对象的产生,例如,我们将默认构造函数,拷贝构造函数声明为私有,就可以防止外界来产生这个对象,这点主要是在单例模式中使用。

在上面我们了解了编译器会自动为对象生成函数的条件。下面来看第二个问题。

在这个问题中,包含了两个构造函数:默认构造函数和拷贝构造函数。下面我们将分别回答这个问题。

1.编译器生成的默认构造函数主要做了什么?

实际这个问题中的表述是不准确的,因为按照标准,编译器只在需要的时候产生才产生一个符合编译器要求的默认构造函数,其他情况下是不会产生一个默认构造函数,因为是不需要的。那么什么是需要的时候呢?答案如下:

>1.内部的成员变量拥有默认构造函数,如果有多个成员变量,那么会按照成员变量声明的顺序来调用成员变量的默认构造函数。

>2.基类拥有默认构造函数。在子类构造的时候,需要先构造父类。

>3.类中声明有虚函数,因为编译器需要为类中的虚函数表指针指定正确的地址。

>4.带有虚基类(virtual base class)。因为编译器需要确定下来虚基类在对象中的偏移,以方便调用虚基类中数据。

上面四种情况下,编译器会为对象合成默认构造函数,而通过上面的情况,也可以知道编译器合成的默认构造函数做了什么(此知识点在《深度探索C++对象模型中》详细描述)。同时也要注意一点:编译器合成的默认构造函数并没有初始化成员变量,如果要为成员变量在构造是指定特定的值,需要在自定义的构造函数中来指定。

第一个问题可以告一段落,下面我们来看默认拷贝构造函数做了什么。

回答这个问题前,我们再来回顾下拷贝构造函数的调用时机,在以下三种情况下会调用对象的拷贝构造函数

>1.以一个对象的值作为另一个对象的初值。例如:

class Obj{};
Obj a;
Obj b = a;
Obj c(a);

>2.当作为函数的参数时。例如:

class Obj{};
void Foo(Obj obj);

>3.当作为函数的返回值时。例如:

class Obj{};
Obj foo()
{
Obj obj;
return obj;//调用拷贝构造函数。
}

由上面第二种情况可知,**在函数中使用引用传参可以减少对象的构造**。

了解完拷贝构造函数的调用时机,我们再来看看编译器生成的默认拷贝构造函数都做了什么。默认拷贝构造函数主要作用是按位拷贝,在必要的时候,除了按位拷贝,还插入一些其他行为,具体内容请看先前的文章----[C++拷贝构造函数总结](http://www.cnblogs.com/yetuweiba/p/3390853.html)。在实现拷贝构造函数中我们需要注意一点深拷贝和浅拷贝,防止出现的拷贝不完全的错误。

以上就是构造函数的语法基础知识点,根据上面的原理,可以总结出来一些容易出错的地方,具体请看下面。

>1.如果对象拥有成员变量,需要在自定义构造函数中设定初值,尽可能地使用成员初始化列表对成员变量进行初始化。同时,成员变量是按照声明的顺序来构造的,所以,要注意依赖。

在构造函数中对成员变量进行初始化的话,在编译器中实际上是先调用此成员变量的构造函数,再调用它的赋值函数,所以,使用成员初始化列表会节省效率。

成员变量的构造是按照声明的顺序进行的,所以,不要让声明早的成员变量依赖声明晚的成员变量。示例如下

class Obj
{
private:
int a;
int b;
// 下面的构造函数是错误的
Obj(const int value)
: a(b + ) // error,此时b还未构造好,a的值会不可预料。
, b(value) // 正确的做法是调换a和b的声明顺序,在成员初始话列表中也调换a和b的顺序
{}
};

>2.谨慎在构造函数中调用虚函数(最好不要在构造函数中调用虚函数)。

我们先来举一个例子:

class Base
{
Base()
{
fun();//error,此时调用的是Base的fun,会发生调用错误。
}
virtual void fun() = ;
};
class Derivate : public Base
{
Derivate(){};
virtual void fun()
{
std::cout << "I am Derivate" << std::endl;
}
}; Derivate d;

上面代码中,构造d会发生错误,因为基类早于派生类进行构造,在基类构造的时候,是不会下降到派生类中的,也就是此时只会调用基类的函数,而Base中的fun是存虚函数,此时就会发生错误。虽然我们可以在Derivate的构造函数中调用fun,但这样做,就意味着接口的设计出现了问题。所以,最好不要在构造函数中调用虚函数。

>3.在并发环境下,注意构造函数的安全性。

当在并发的环境下,原本简单的事情就会变的复杂。我们都知道,在并发环境下,线程的执行是乱序的,我们拿到一个指向对象的指针,这个指针指向的可能是构造函数执行一般的对象,例如:

class Foo : public Observer
{
// error
Foo(Observable * s)
{
s->register(this)
}
}

上面例子的做法是不安全的,因为在构造函数中将自身注册出去,此时对象可能仍未完全构造完。如果,Foo是积累的话,那么注册的时候派生类还没有构造完成,此时也会引发错误。所以,由上例可知:在对象构造期间不要对外暴露this指针(陈硕)。解决上面错误的一个方法就是“二段式构造”,即对象构造完成后,再对外暴露this指针。


以上就是关于C++中构造函数的一些总结,如果有错误的地方,请大家多多指教,谢谢。另外,不支持Markdown,有些不方便。

C++的构造函数总结的更多相关文章

  1. .NET 基础 一步步 一幕幕[面向对象之构造函数、析构函数]

    构造函数.析构函数 构造函数: 语法: //无参的构造函数 [访问修饰符] 函数名() :函数名必须与类名相同. //有参的构造函数 [访问修饰符] 函数名(参数列表):函数名必须与类名相同. 作用: ...

  2. javascript工厂模式和构造函数模式创建对象

    一.工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程(本书后面还将讨论其他设计模式及其在JavaScript 中的实现).考虑到在ECMAScript 中无法创 ...

  3. JS继承之借用构造函数继承和组合继承

    根据少一点套路,多一点真诚这个原则,继续学习. 借用构造函数继承 在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数(constructor stealing)的技术( ...

  4. PHP与JAVA构造函数的区别

    早期的PHP是没有面向对象功能的,但是随着PHP发展,从PHP4开始,也加入了面向对象.PHP的面向对象语法是从JAVA演化而来,很多地方类似,但是又发展出自己的特色.以构造函数来说,PHP4中与类同 ...

  5. C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义 ...

  6. golang语言构造函数

    1.构造函数定义 构造函数 ,是一种特殊的方法.主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中.特别的一个类可以有多个构造函数 ,可根据其参数个 ...

  7. 前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型

    前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型 前言(题外话): 有人说拖延症是一个绝症,哎呀治不好了.先不说这是一个每个人都多多少少会有的,也不管它究竟对生活有多么大的 ...

  8. Aop动态生成代理类时支持带参数构造函数

    一.背景 在某些情况下,我们需要植入AOP代码的类并没有默认构造函数.那么此时动态生成的代理类也需要相同签名的构造函数,并且内部调用原始类的构造函数.自己折腾了1晚上没搞定,现在搞定了发出来供大家一起 ...

  9. C#的泛型的类型参数可以有带参数的构造函数的约束方式吗?

    Review后看到标题让我十分羞愧自己语文功底太差,估计...请见谅......我还特地把这句写回开头了...... 问题 前天遇到的一个问题,所以在MSDN发了个问,刚也丰富了下问题,关于泛型的. ...

  10. Android中自定义样式与View的构造函数中的第三个参数defStyle的意义

    零.序 一.自定义Style 二.在XML中为属性声明属性值 1. 在layout中定义属性 2. 设置Style 3. 通过Theme指定 三.在运行时获取属性值 1. View的第三个构造函数的第 ...

随机推荐

  1. 使用Python,字标注及最大熵法进行中文分词

    使用Python,字标注及最大熵法进行中文分词 在前面的博文中使用python实现了基于词典及匹配的中文分词,这里介绍另外一种方法, 这种方法基于字标注法,并且基于最大熵法,使用机器学习方法进行训练, ...

  2. ext3中xtype属性汇总

    基本组件: xtype Class 描述 button Ext.Button 按钮 splitbutton Ext.SplitButton 带下拉菜单的按钮 cycle Ext.CycleButton ...

  3. python3 urllib.request.urlopen() 地址打开错误

    错误内容:UnicodeEncodeError: 'ascii' codec can't encode characters in position 28-29: ordinal not in ran ...

  4. BuguMongo是一个MongoDB Java开发框架,集成了DAO、Query、Lucene、GridFS等功能

    http://code.google.com/p/bugumongo/ 简介 BuguMongo是一个MongoDB Java开发框架,它的主要功能包括: 基于注解的对象-文档映射(Object-Do ...

  5. IE常见的CSS的BUG(一)

    2011年6月,我毕业了.2012年我接触了CSS,本以为会好过些能赚点钱了,可谁知,Internet Explorer(下称IE),这个前端工程师的噩梦浏览器让我不再那么好过了.各种出现在IE身上的 ...

  6. centos6.5配置无线网络

    由于安装的是服务器版,所以开机无法连接网络,以下这些情况都是针对驱动已经安装OK.按步骤操作如下(以下操作默认都是在超级管理员权限下进行): 1.测试电脑是否安装wpa_supplicant,测试方法 ...

  7. easyui-combobox默认值绑定

    $('#combox_role').combobox({ panelHeight: , url: '../../Handler/GetComboxItems.ashx?type=0', valueFi ...

  8. FileShare文件读写锁解决“文件XXX正由另一进程使用,因此该进程无法访问此文件”(转)

    开发过程中,我们往往需要大量与文件交互,读文件,写文件已成家常便饭,本地运行完美,但一上到投产环境,往往会出现很多令人措手不及的意外,或开发中的烦恼,因此,我对普通的C#文件操作做了一次总结,问题大部 ...

  9. VS2010不能打开预编译的网站源码的原因是什么?(转之csdn)

    原问题: 今天将写好的一个网站源码目录拷贝到另一台电脑上,但打开时提示:    你要打开一个预编译的网站,你可以查看该站点,但对它进行更改可能会造成该网站停止运行,若要修改站点,建议先编辑原始网站中的 ...

  10. 判别linux机器字节序为大端还是小端

    代码如下: #include <iostream> #include <arpa/inet.h> #include <cstdio> using namespace ...