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

  

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. Java基础——枚举

    一.使用枚举类之前是如何实现枚举的  在JDK1.5之前,我们定义常量都是:public static fianl....:定义枚举也可以通过如下的方式: package com.jiangbei.t ...

  2. 20155310 2016-2017-2 《Java程序设计》第四周学习总结

    20155310 2016-2017-2 <Java程序设计>第四周学习总结 一周两章新知识的自学与理解真的是很考验和锻炼我们,也对前面几章我们的学习进行了检测,遇到忘记和不懂的知识就再复 ...

  3. 20155318 2016-2017-2 《Java程序设计》第一周学习总结

    20155318 2016-2017-2 <Java程序设计>第一周学习总结 教材学习内容总结 上周总结 上周学习了一些大学的学习方法,比如知识分为为三种:元知识.软知识和硬知识,讲述技能 ...

  4. 2017-2018-2 《网络对抗技术》 20155322 第二周 Exp1 PC平台逆向破解(5)M

    #2017-2018-2 <网络对抗技术> 20155322 第二周 Exp1 PC平台逆向破解(5)M [博客目录] 1-实践目标 1.1-实践介绍 1.2-实践内容 1.3-实践要求 ...

  5. 20155330 实验三 敏捷开发与XP实践

    20155330 实验三 敏捷开发与XP实践 实验内容 XP基础 XP核心实践 相关工具 实验步骤 (一)敏捷开发与XP 软件工程是把系统的.有序的.可量化的方法应用到软件的开发.运营和维护上的过程. ...

  6. Drupal7 配置多站点及为每个站点设置语言

    默认情况 在Drupal7的安装目录下存在sites目录 sites目录结构如下: --all --default --example.sites.php --README.txt 1. 添加新域名, ...

  7. cookie和session在Django中的应用

    1 会话跟踪技术 什么是会话跟踪 我们需要先了解一下什么是会话!可以把会话理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多次请求和响应.例如你给10086打个电话,你就是客户端,而1008 ...

  8. NPOI读取Excel到集合对象

    之前做过的项目中有个需要读取Excel文件内容的需求,因此使用NPOI实现,写下以下代码,这个只是一个代码段,还有很多地方需要优化,希望能对大家有所帮助 public static IList< ...

  9. 04-JVM内存模型:直接内存

    1.1.什么是直接内存(Derect Memory) 在内存模型最开始的章节中,我们画出了JVM的内存模型,里面并不包含直接内存,也就是说这块内存区域并不是JVM运行时数据区的一部分,但它却会被频繁的 ...

  10. localhost/127.0.0.1/本机IP的区别以及端口号

    端口号: http请求默认的端口是:80 PHPstudy中的端口号: Apache服务器的端口是:80 MySQL数据库的端口是:3306 PHP项目端口是:9000 禅道中的端口号: Apache ...