构造函数是特殊的成员函数,只要创建类类型的对象,都要执行构造函数。构造函数的工作是保证每个对象的数据成员具有合适的初始值。

  

class Sales_Item {
public:
//operations on Sales_item objects
//default constructor needed to initialize members of built-in type
Sales_item(): units_sold(), revenue(0.0){}
private:
std::string isbn;
unsigned units_solds;
double revenue;
};

  构造函数的名字与类的名字相同,并且不能指定返回类型。像其他任何函数一样,它们可以没有形参,也可以定义多个形参。

构造函数的几个特性:

  1. 构造函数可以重载

  2. 构造函数不能声明为const

    

 class Sales_item {
public:
Sales_item() const; //error
};

  constg构造函数是不必要的。创建类类型的const对象时,运行一个普通构造函数来初始化该const对象。构造函数的工作是初始化对象。不管对象是否为const,都用一个构造函数初始化该对象。

构造函数初始化式:

 Sales_item::Sales_item(const string &book):
isbn(book), units_sold(), revenue(0.0) {}

 构造函数初始化列表以一个冒号开始,接着是一个逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号里中的初始化式。这个构造函数将isbn成员初始化为book形参的值,将units_sold和revenue初始化为0。与任意的成员函数一样,构造函数可以定义在类的内部或外部。构造函数初始化式只在构造函数的定义中而不在声明中指定。

  构造函数初始化列表难以理解的一个原因在于,省略初始化列表并在构造函数的函数体内对数据成员赋值是合法的。例如,可以将接受一个string的Sales_item构造函数编写为:

//legal but sloppier way to write constructor
// no constructor initializer
Sales_item::Sales_item(const string &book) {
isbn = book;
units_sold = ;
revenue = 0.0;
}

这个构造函数给类Sales_item的成员赋值,但没有进行显式初始化。不管有没有显式的初始化式,在执行构造函数之前,要初始化化isbn成员。这个构造函数隐式使用默认的string构造函数初始化isbn。执行构造函数的函数体时,isbn已经有值了。该值被构造函数函数体中的赋值所覆盖。

  从概念上讲,可以认为构造函数分两个阶段:(1)初始化阶段;(2)普通的计算阶段

  不管成员是否在构造函数初始化列表中显式初始化,类类型的数据成员总是在初始化阶段初始化(调用默认构造函数,若没有默认构造函数,则编译错误)。初始化发生在计算阶段开始前。

  构造函数初始化列表中没有显式提及的每个成员,使用与初始化变量相同的规则进行初始化:

  1)类类型:运行该类型的默认构造函数

  2)内置或复合类型的成员的初始值依赖于对象的作用域;在局部作用域中这些成员不被初始化,而在全局作用域中它们初始化为0;

  

  因为内置类型的成员不进行隐式初始化,所以对这些成员是进行初始化还是赋值似乎无关紧要。但对于类类型的数据成员若未在初始化列表当中显式初始化,而是在函数体里赋值,则相当于先调用类的默认构造函数初始化,再在函数体里赋值,故相比于直接利用初始化列表,效率较低。

  如果没有为类提供初始化式,则编译器会隐式地使用成员类型的默认构造函数,如果那个类没有默认构造函数,则编译器尝试使用默认构造函数失败。

  必须利用初始化列表的几种情况:

  1)没有默认构造函数的类类型的成员;

  2)const类型的成员;

  3)引用类型的成员;

  上述类型必须在初始化列表中进行初始化,在函数体中对他们赋值是不起作用的。

1 class ConstRef {
2 public:
3 ConstRef(int ii)'
4 private:
5 int i;
6 const int ci;
7 int &ri;
8 };
9 //no explicit constructor: error ri is uninitialized
10 ConstRef::Constef(int ii)
11 {
12 i = ii; //ok
13 ci = ii; //error:cannot assign to a const;
14 ri = i; //assignes to ri which was not bound to an objects
15 }   可以初始化const对象或引用类型的对象,但不能对它们赋值。在开始开始执行构造函数的函数体之前,要完成初始化。初始化const或引用类型数据成员唯一的机会是在构造函数初始化列表中。

成员初始化的次序:

  每个成员在初始化列表中只能指定一次。

  成员被初始化的顺序就是成员定义的次序,而与他们在构造函数的初始化列表中的顺序无关。

 

默认构造函数:
  
只要定义一个对象时没有提供初始化式,就使用默认构造函数。为所有形参提供默认实参的构造函数也定义了默认构造函数。

  一个类哪怕只定义一个构造函数(包括复制构造函数),编译器也不会再生成默认构造函数。只有当一个类没有定义默认构造函数,编译器才自动生成一个。

  如果类包含内置或复合类型(数组)的成员,则该类不依赖于合成的默认构造函数。它应该定义自己的默认构造函数。

  合成的默认构造函数使用与变量初始化相同的规则来初始化成员。此外,每个构造函数应该为每个内置或复合类型的成员提供初始化式。没有初始化内置或复合类型成员的构造函数,将使那些成员处于未定义状态。除了作为赋值的目标外,以任何方式使用一个为定义的成员都是错误的。

使用默认构造函数:

 Sales_item myobj();

编译myobj的声明没有问题,然而,使用myobj时会出错。原因是myobj的定义被编译器解释为一个函数的声明。使用默认构造函数定义一个对象的正确方式是去掉最后的空括号:

 Sales_item myobj;

下面的代码也是正确的:

  Sales_item myobj = Sales_item();

在这里,我们创建并初始化一个Sales_item对象,然后用它来按值初始化myobj。编译器通过运行Sales_item的默认构造函数来按值初始化一个Sales_item。

没有默认构造函数的后果:

  假设有一个NoDefaultl,它没有定义自己的默认构造函数,却有一个接受一个string实参的构造函数。因为该类定义了一个构造函数,因此编译器将不会合成默认构造函数。NoDefault没有默认构造函数,一位置:

 1)具有Nodefault成员的每个类的每个构造函数,必须传递一个初始的string值给Default构造函数来显式地初始化NoDefault成员。

 2)编译器将不会为具有NoDefault类型成员的类合成默认构造函数。如果这样的类希望提供默认构造函数,就必须显式地定义,并且默认构造函数必须显式地初始化其Nodefault成员。

 3)NoDefault类型不能用作动态分配数组的元素。如果没有为类类型数组提供初始化式,则将用默认构造函数初始化每个元素。

 4)NoDefault类型的静态分配数组必须为每个元素提供一个显式的初始化。

 5)如果有一个保存NoDefault对象的容器,例如vector,就不能使用接受容器大小而没有同时提供一个元素初始化的构造函数。编译器首先调用默认构造函数创建

  一个临时值来初始化,然后使用复制构造函数复制到容器的每个元素。

 即以下都是错误的:

  

NoDefault nd;
NoDefault nd[];
NoDefault *pa = new A[];
std::vector<NoDefault> vec();

下面哪些陈述不正确:

a)类必须提供至少一个构造函数

  不正确。因为类也可以不提供构造函数,这时使用由编译器合成的默认构造函数。

b)默认构造函数的形参列表中没有形参。

  不正确。因为为所有形参都提供了默认实参的构造函数也定义了默认构造函数,而这样的构造函数形参列表中是有形参的。

c)如果一个类没有有意义的默认值,则该类不应该提供默认构造函数

  不正确。因为如果一个类没有默认构造函数(指的是该类提供了构造函数,但没有提供自己的默认构造函数),则在编译器需要隐式使用默认构造函数的环境中,该类就不能使用,所以,一个类定义了其他构造函数,则通常也应该提供一个默认构造函数。

d)如果一个类没有定义默认构造函数,则编译器会自动生成一个,同时将每个数据成员初始化为相关类型的默认值。

  不正确。因为编译器合成的默认构造函数,不是将每个数据成员初始化行为相关类型的默认值,而是使用与变量初始化相同的规则来初始化成员;类类型的成员执行各自的默认构造函数进行初始化;内置和复合类型的成员,只对定义在全局作用域中的对象才初始化。

【C++】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. Flex copy and paste

    <?xml version="1.0" encoding="utf-8"?> <mx:WindowedApplication xmlns:mx ...

  2. 20155217 实验四《Java面向对象程序设计》实验报告

    20155217 实验四<Java面向对象程序设计>实验报告 一.实验内容 1.基于Android Studio开发简单的Android应用并部署测试; 2.了解Android.组件.布局 ...

  3. 【转】odoo学习之:开发字段解析

    odoo新API中,字段类型不变,继承改变 1.旧的API定义模型: from openerp.osv import osv,fields class oldmodel(osv.osv): #模型名称 ...

  4. [转载]A cycle was detected in the build path of project

    解决Eclipse中Java工程间循环引用而报错的问题 如果我们的项目包含多个工程(project),而它们之间又是循环引用的关系,那么Eclipse在编译时会抛出如下一个错误信息: “A cycle ...

  5. youtube高清视频下载方法

    youtube下载方法有多种, 但都不支持1080P以上的高清下载, 今天找到一种支持1080P的, 记录一下 步骤1: 百度搜: Dooseen tubedown 下载该软件, 并安装, 一直下一步 ...

  6. Linux目录与文件操作

    文件命名规则: 1.严格区分大小写: 2.长度不能超过255个字符: 3.不能使用/当文件名 mkdir:创建空目录 -p:parent,父目录,逐级创建 -v:verbose,打印详细信息 命令行展 ...

  7. KRKR基础篇(二)

    这里介绍一些krkr的语法规范,具体的命令含义及用法以后再叙述 一:kag语法及基本概念 KAG使用的剧本语言为KAG Script,文件扩展名为.ks 脚本内的文字除  注释,  命令 ,  段落标 ...

  8. centos上搭建git服务--2

    在 Linux 下搭建 Git 服务器   环境: 服务器 CentOS6.6 + git(version 1.7.1)客户端 Windows10 + git(version 2.8.4.window ...

  9. CF 1008B Turn the Rectangles(水题+贪心)

    There are n rectangles in a row. You can either turn each rectangle by 90 degrees or leave it as it ...

  10. Python Requests库简单入门

    我对Python网络爬虫的学习主要是基于中国慕课网上嵩天老师的讲授,写博客的目的是为了更好触类旁通,并且作为学习笔记之后复习回顾. 1.引言 requests 库是一个简洁且简单的处理HTTP请求的第 ...