一、构造函数初始化列表

推荐在构造函数初始化列表中进行初始化
构造函数的执行分为两个阶段

初始化段

普通计算段

(一)、对象成员及其初始化

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

 
#include <iostream>


using 
namespace std;

class Object

{


public:

    Object(
int num) : num_(num)

    {

        cout << 
"Object " << num_ << 
" ..." << endl;

    }

    ~Object()

    {

        cout << 
"~Object " << num_ << 
" ..." << endl;

    }


private:

    
int num_;

};

class Container

{


public:

    Container(
int obj1 = 

int obj2 = 
) : obj2_(obj2), obj1_(obj1)

    {

        cout << 
"Container ..." << endl;

    }

    ~Container()

    {

        cout << 
"~Container ..." << endl;

    }

private:

    Object obj1_;

    Object obj2_;

};

int main(
void)

{

    Container c(

);

    
return 
;

}

从输出可以看出几点,一是构造对象之前,必须先构造对象的成员;二是对象成员构造的顺序与定义时的顺序有关,跟初始化列表顺序无关;三是构造的顺序和析构的顺序相反;四是如果对象成员对应的类没有默认构造函数,那对象成员也只能在初始化列表进行初始化。

(二)、const成员、引用成员的初始化

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

 
#include <iostream>


using 
namespace std;

// const成员的初始化只能在构造函数初始化列表中进行
// 引用成员的初始化也只能在构造函数初始化列表中进行
// 对象成员(对象成员所对应的类没有默认构造函数)的初始化,也只能在构造函数初始化列表中进行

class Object

{


public:

    
enum E_TYPE

    {

        TYPE_A = 
,

        TYPE_B =

};


public:

    Object(
int num = 
) : num_(num), kNum_(num), refNum_(num_)

    {

        
//kNum_ = 100;
        
//refNum_ = num_;
        cout << 
"Object " << num_ << 
" ..." << endl;

    }

    ~Object()

    {

        cout << 
"~Object " << num_ << 
" ..." << endl;

    }

void DisplayKNum()

    {

        cout << 
"kNum=" << kNum_ << endl;

    }


private:

    
int num_;

    
const 
int kNum_;

    
int &refNum_;

};

int main(
void)

{

    Object obj1(
);

    Object obj2(
);

    obj1.DisplayKNum();

    obj2.DisplayKNum();

cout << obj1.TYPE_A << endl;

    cout << obj2.TYPE_A << endl;

    cout << Object::TYPE_A << endl;

return 
;

}

因为const 变量或者引用都得在定义的时候初始化,所以const 成员和引用成员必须在初始化列表中初始化。另外,可以使用定义枚举类型来得到类作用域共有的常量。

二、拷贝构造函数

(一)、拷贝构造函数

功能:使用一个已经存在的对象来初始化一个新的同一类型的对象
声明:只有一个参数并且参数为该类对象的引用 Test::Test(const Test &other) ;
如果类中没有定义拷贝构造函数,则系统自动生成一个缺省复制构造函数,作为该类的公有成员,所做的事情也是简单的成员复制

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

 
#ifndef _TEST_H_


#define _TEST_H_

class Test

{


public:

    
// 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的
    
// 默认的构造函数
    Test();

    
explicit Test(
int num);

    Test(
const Test &other);

    
void Display();

Test &
operator=(
const Test &other);

~Test();


private:

    
int num_;

};


#endif 
// _TEST_H_

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

 
#include 
"Test.h"


#include <iostream>


using 
namespace std;

// 不带参数的构造函数称为默认构造函数
Test::Test() : num_(
)

{

    
//num_ = 0;
    cout << 
"Initializing Default" << endl;

}

Test::Test(
int num) : num_(num)

{

    
//num_ = num;
    cout << 
"Initializing " << num_ << endl;

}

Test::Test(
const Test &other) : num_(other.num_)

{

    
//num_ = other.num_;
    cout << 
"Initializing with other " << num_ << endl;

}

Test::~Test()

{

    cout << 
"Destroy " << num_ << endl;

}

void Test::Display()

{

    cout << 
"num=" << num_ << endl;

}

Test &Test::
operator=(
const Test &other)

{

    cout << 
"Test::operator=" << endl;

    
if (
this == &other)

        
return *
this;

num_ = other.num_;

    
return *
this;

}

 C++ Code 

1

2

3

4

5

6

7

8

9

10

 
#include 
"Test.h"

int main(
void)

{

    Test t(
);

    
//Test t2(t);       // 调用拷贝构造函数
    Test t2 = t;        
// 等价于Test t2(t);

return 
;

}

即调用了拷贝构造函数,destroy 的两个分别是t 和 t2。

(二)、拷贝构造函数调用的几种情况

当函数的形参是类的对象,调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参拷贝到新的对象中。理所当然也调用拷贝构造函数。

当函数的返回值是类对象,函数执行完成返回调用者时使用。理由也是要建立一个临时对象中,再返回调用者。为什么不直接用要返回的局部对象呢?因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以在处理这种情况时,编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。所谓return 对象,实际上是调用拷贝构造函数把该对象的值拷入临时对象。如果返回的是变量,处理过程类似,只是不调用构造函数。

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

 
#include 
"Test.h"


#include <iostream>


using 
namespace std;

void TestFun(
const Test t1)

{

}

void TestFun2(
const Test &t1)

{

}

Test TestFun3(
const Test &t1)

{

    
return t1;

}

const Test &TestFun4(
const Test &t1)

{

    
//return const_cast<Test&>(t1);
    
return t1;

}

int main(
void)

{

    Test t(
);

    TestFun(t);

cout << 
"........" << endl;

return 
;

}

即在传参的时候调用了拷贝构造函数,函数返回时TestFun 的形参t 1生存期到了,在分割线输出之前销毁t1,最后destroy 的是 t。

将TestFun(t); 换成 TestFun2(t);

参数为引用,即没有调用拷贝构造函数。

将TestFun(t); 换成 t = TestFun3(t);

函数返回时会调用拷贝构造函数,接着调用赋值运算符,释放临时对象,最后释放t。如果没有用t 接收,则临时对象也会马上释放。

将TestFun(t); 换成 Test t2 = TestFun3(t);

函数返回调用拷贝构造函数,但没有再次调用拷贝构造函数,而且没有释放临时对象,可以理解成临时对象改名为t2 了。

将TestFun(t); 换成 Test& t2 = TestFun3(t);

函数返回时调用拷贝构造函数,因为t2 引用着临时对象,故没有马上释放。

将TestFun(t); 换成 Test t2 = TestFun4(t);

函数传参和返回都没有调用拷贝构造函数,初始化t2 时会调用拷贝构造函数。

将TestFun(t); 换成 const Test&  t2 = TestFun4(t);

函数传参和返回都没有调用构造函数,t2 是引用故也不会调用拷贝构造函数。

从零开始学C++之构造函数与析构函数(二):初始化列表(const和引用成员)、拷贝构造函数的更多相关文章

  1. C++中构造函数的初始化列表(const、引用&变量初始化)

    1. 构造函数执行分为两个阶段: a.初始化阶段(初始化) 初始化阶段具体指的是用构造函数初始化列表方式来初始化类中的数据成员. ClassXX:val(a),key(b){}; b.普通计算阶段(赋 ...

  2. 初始化列表(const和引用成员)、拷贝构造函数

    一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 构造函数的执行分为两个阶段 初始化段 普通计算段 (一).对象成员及其初始化  C++ Code  1 2 3 4 5 6 7 8 9 1 ...

  3. 从零开始学 Web 之 JS 高级(二)原型链,原型的继承

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  4. 从零开始学 Web 之 移动Web(二)JD移动端网页,移动触屏事件

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  5. 从零开始学 Web 之 Vue.js(二)过滤器,按键修饰符,自定义指令

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  6. 从零开始学 Web 系列教程

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新…… github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:http:/ ...

  7. C++构造函数和析构函数,以及构造函数特殊成员变量和函数的初始化

    body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...

  8. C++:派生类的构造函数和析构函数的调用顺序

    一.派生类 在C++编程中,我们在编写一个基类的派生类时,大致可以分为四步: • 吸收基类的成员:不论是数据成员还是函数成员,派生类吸收除基类的构造函数和析构函数之外的全部成员. • 改造基类函数:在 ...

  9. C++ 类 & 对象-类成员函数-类访问修饰符-C++ 友元函数-构造函数 & 析构函数-C++ 拷贝构造函数

    C++ 类成员函数 成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义. 需要强调一点,在 :: 运算符之前必须使用类名.调用成员函数是在对象上使用点运算符(.),这样它就能操作与 ...

随机推荐

  1. php——SoapClient访问webservice

    原文:php--SoapClient访问webservice 通过SoapClient调用天气预报 <?phpheader ( "Content-Type: text/html; ch ...

  2. 如何查看IC卡燃气表读数和剩余量?

    如今新建的楼房都是使用IC卡燃气表,拿到房子入住时也没有见到IC卡燃气表的使用说明书.非常多人可能为此而苦恼.这里就讲一下怎样查看IC卡燃气表读数及剩余金额. 产品外观 可选功能 watermark/ ...

  3. PHP 9: 表达式

    原文:PHP 9: 表达式 本章介绍PHP的表达式.PHP的表达式其实和其他语言没有什么区别.普通的赋值是表达式,函数也是表达式,通过函数赋值也是.三元条件运算符也是,即: $first ? $sec ...

  4. PHP 8: PHP的运算符

    原文:PHP 8: PHP的运算符 本章将介绍PHP的运算符.运算符这个问题在每种语言里都有,因为我们已经熟悉了编程语言里的一种或是多种,所以只需要了解一下就行了.概括一下吧.PHP运算符有很多种,看 ...

  5. Java 之复合赋值运算符

    1.引入问题 切入正题,看下面代码,结果应该是怎么样的 public class App{ public static void main( String[] args ){ byte a=1 ; i ...

  6. C#6.0 中的那些新特性

    C#6.0 中的那些新特性 前言 VS2015在自己机器上确实是装好了,费了老劲了,想来体验一下跨平台的快感,结果被微软狠狠的来了一棒子了,装好了还是没什么用,应该还需要装Xarmain插件,配置一些 ...

  7. javascript 函数介绍

    javascript函数使用的时候,往往都比较单一,这里介绍几种不同于我们之前使用的函数调用方式! 1.函数表达式包含名称,用于递归 var f = function s(num) { if (num ...

  8. C注意,使用的语言字符串

    转载请注明出处! 在C语言没有具体的字符串数据类型,字符串的字符串常量和字符数组的形式. 实际上该字符串是零个或更多字符的字符串.并在整个位模式0NUL字节结束.因此,字符串所包括的字符内部不能出现N ...

  9. android的版本控制

    在一个团队项目中,项目的版本控制,一方面促进团队的协同能力,另一方面提高项目的进度 即使一个人的项目也最好使用SVN这样的版本控制,可以清楚的知道自己的修改的代码,和项目进度等.合理的托管,也让优秀的 ...

  10. Ibatis ISqlMapper工厂类案例

    namespace Model{ public class MapperFactory { //声明一个ISqlMapper接口类型的数据映射器 _mapper,其初始值为null private s ...