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

  

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. 20155218 《Java程序设计》实验五(网络编程与安全)实验报告

    20155218 <Java程序设计>实验五(网络编程与安全)实验报告 一.实验内容及步骤 (一) 编写MyBC.java实现中缀表达式转后缀表达式的功能 编写MyDC.java实现从上面 ...

  2. git clone的时候报error: RPC failed; result=18错误

    因业务需求,需要把内网gitlab仓库的地址对外网访问,在gitlab前端配置了一个nginx代理服务器,来实现需求,可以在git clone的时候报error: RPC failed错误 [root ...

  3. 海思NB-IOT的SDK函数使用说明

    1. 查询当前AT指令是否正在处理中 while(get_at_cmd_in_progress() == false); 2. 信号量发送函数 (, osNoWait); 3. 信号量接收函数 if( ...

  4. idea 严重: Error configuring application listener of class org.springframework.web.context.Context 后面省略

    根本原因:jar文件没有同步发布到自己项目的lib目录中 解决方案:把之前在这个位置的jar文件,put into 到 /WEB-INF/lib 目录下即可

  5. 搜索引擎Solr6.2.1 索引富文本(word/pdf/txt/html)

    一:首先建立Core 在core下面新建lib文件夹,存放相关的jar包,如图所示: lib文件夹打开所示,这些类库在solr6.2.1解压之后都能找到: 修改solrconfig.xml,把刚刚建的 ...

  6. asp.net 问题:Web 服务器上的请求筛选模块被配置为 拒绝包含的查询字符串过长的请求

    发现问题: post请求,在发送一个图片base64编码的字符串时,服务端报这个错误. 报错信息中给出了解决办法: 最可能的原因: Web 服务器上的请求筛选被配置为拒绝该请求,因为查询字符串过长. ...

  7. loadrunner12安装教程

    全套五个文件: 独立安装包,插件包,LR安装包,语言包,版本说明书 loadrunner 12安装教程 1.首先下载Loadrunner12安装包.下载下来将会有四个安装包. HP_LoadRunne ...

  8. 用Micro:bit做床头灯

    这是一个非常简单的项目,给孩子们介绍感应和控制,使用光敏电阻LDR作为光线传感器和床头灯的LED. 这也介绍了模拟输入的概念.数字输入为ON或OFF.只有0和1两种可能的条件.仿真输入是一系列可能值中 ...

  9. 1.5 JAVA的高并发编程

    一.多线程的基本知识 1.1进程与线程的介绍(上个博客1.4中已经详细介绍进程和线程) 程序运行时在内存中分配自己独立的运行空间,就是进程 线程:它是位于进程中,负责当前进程中的某个具备独立运行资格的 ...

  10. [寒假学习笔记](一)Markdown语法学习

    Markdown 学习 在博客园上使用markdown编辑,记录学习进度,以来日可以复习 前期准备 1. 安装markdownpad2 官网直接找下载安装,遇到bug他会自动提示信息,跟着提示去安装一 ...