C++ 构造函数和析构函数(Constructors & Destructors)
一、定义:
当object产生,有一个特殊的称为constructor的函数会自动执行。当object死亡,有一个特殊的称为destructor的函数会自动执行。Constructor 可以不只一个,但 destructor 只能有一个。
Constructor(构造函数)就是与class同名的那些member functions,例如CPoint::CPoint()或CDemo::CDemo()。Constructors不能够指定返回值类型,也就是它不必(但可以)return。constructions可以有一个或多个,各有不同类型的参数。
Destructor(析构函数)就是与class同名,且前面有加“~”符号的那个member function,例如例如 CPoint::~CPoint() 或 CDemo::~CDemo()。Destructor 不能指定返回值类型,也就是它不必(但可以)return。每个class只有一个destructor,并且不能有任何参数。
由于global object的诞生比程序进入更早点,所以global object的constructor执行的时间更早于程序的进入点。
二、Default Constructors
所谓的default constructor就是没有指定任何的参数的constructor。如果我们的class CA声明如下:
1 #include <iostream>
2 using namespace std;
3 class CA
4 {
5 public:
6 int getdata(){return _data;};
7 CA(){_data =5;};
8 CA(int val){_data =val;};
9
10 protected:
11 int _data;
12
13 };
CA有两个constructors,分别是CA(int) 和 CA()。后都没有参数,正是所谓的default constructor。当我产生一个 CA object 而沒有指定任何参数:
1 CA aCA;
2 CA* pCA = new CA;
编译器就为我们呼叫default constructor。
普遍存在于C++程序员之间的一个误解是:如果我们没有为某个class设计constructor,编译器会自动为该class制造出一个default constructor来。这个说法也对也不对,这里我先提示一个结论,销稍后有更多细节探讨。什么是编译器所需要的动作?就是隐藏在C++程序代码下面,让C++诸多特征得到实现的动作,包含以下三种情况:
1. class CA 內 含 class CZ objects,如图1:当产生一个CA object 时,隐藏在底层的必要动作是先初始化CZ object(因为CA object中有一个CZ object)
2.class CA继承于class CZ,如图2:当产生一个CA object 时,隐藏在底层的必要动作是先调用CZ的default constructor (因为CA object中有一个CZ subobject)
3.CA是一个 polymorphic class,也就是说它有virtual functions,或继承于有virtual functions的class。当产生一个CA object,隐藏在底层的必要动作是:将虚拟机制所需要的vptr和vtb1初始化。
由于这三种情况皆有所谓的“底层的必要动作”,所以编译器必须自动为class CA生成一个default constructor((如果沒有任何 user-defined constructor 的话),或暗中对已有的user-defined constructor 动作脚(添加一些代码)。生成出来的东西称为“implicit nontrivial default constructor”。
如果沒有上述需求,编译器就不会为class产生一个default constructor,例如:
1 class CB
2 { public:
3 int getdata() { return _data; };
4 protected:
5 int _data;
6 };
virtual functions的base classes),也没有内含embedded objects,也没有继承于其它class,所以编译器不会为它生成一个default constructor出来,于是当我们这么做:
1 CB aCB; // 应该调用default constructor
2 cout << "aCB.getdata()=" << aCB.getdata() << endl;
但得到的结果如下:
aCB.getdata()=4211382 // 莫名其妙的初值
这个恐怕不是我所期望的,我们期望的_data有个初始值,但这个不是编译器的需求,所以我们只能自求多福,自已设计一个default constructor:
1 class CB
2 { public:
3 int getdata() { return _data; };
4 CB() { _data = 5; }; // default constructor
5 protected:
6 int _data;
7 };
现在输出的结果就是我们所期望的了:
CB aCB; // 应该调用 default constructor
cout << "aCB.getdata()=" << aCB.getdata() << endl;
// 输出结果:aCB.getdata()=5
注意,如果class已经有了任何constructor,但不是default constructor,编译器绝不会为它生成一个default constructor。如果class CZ正是如此一个class,那么当你想产生一个CZ object,并且没有指定参数时:
CZ *aCZ; // error
编译器会输出异常提示:
error C2512: 'CZ' : no appropriate default constructor available
三、Copy Constructors
所谓的Copy Constructors是指有一个参数的类型是其 class type的 constructor,例如:
1 class CA
2 {
3 public:
4 int getdata() {return _data; }; //default constructor
5 CA(){_data =5;};
6 CA(int val){_data=val; }; //constructor
7 CA(const CA& ca){ //copy constructor
8 cout<<"copy constructor"<<endl;
9 _data=10;
10
11 };
12
13 protected:
14 int _data;
15 };
以下两种情况,会唤起copy constructor:
情况1:将一个object当做参数传给某一个函数;
情况2:将一个object当做函数的返回值;
例如(沿用上面的class CA):
1 #include <iostream>
2
3 using namespace std;
4 class CA
5 {
6 public:
7 int getdata() {return _data; }; //default constructor
8 CA(){_data =5;};
9 CA(int val){_data=val; }; //constructor
10 CA(const CA& ca){ //copy constructor
11 cout<<"copy constructor"<<endl;
12 _data=10;
13
14 };
15
16 protected:
17 int _data;
18 };
19
20 void foo2(CA aCA)//情况1(函数参数是个object)
21 {
22 cout<<"in foo2(),aCA.getdata()="<<aCA.getdata()<<endl;
23 }
24
25 CA foo3()//情况2(函数的返回值是个object)
26 {
27 CA aCA(3);
28 cout<<"in foo3(),aCA.getdata()="<<aCA.getdata()<<endl;
29 return aCA;
30 }
31
32 int main()
33 {
34
35 CA aCA1,aCA2(7);
36 cout << "aCA1.getdata()="<<aCA1.getdata()<< endl;
37 cout << "aCA2.getdata()="<<aCA2.getdata()<< endl;
38 aCA2 =aCA1;//object assignment
39 cout << "aCA2.getdata()="<<aCA2.getdata()<< endl;
40 foo2(aCA1);//情况1(调用之前aCA1._data 为5)
41 aCA2=foo3();
42 cout << "aCA2.getdata()="<<aCA2.getdata()<< endl;
43
44 return 0;
45 }
让我们看看执行结果:
aCA1.getdata()=5
aCA2.getdata()=7
aCA2.getdata()=5 // 经过 object assignment 之后
copy constructor
in foo2(), aCA.getdata()=10 // 经过情況 1 之后
in foo3(), aCA.getdata()=3
copy constructor
aCA2.getdata()=10 // 经过情況 2 之后
这里有几点需要注意的:
1. 38行的将一个object指派(assign)给另一个object,这也是一种复制,但它唤起的所谓的copy assignment operator。本例并没有特别设计copy assignment operator;
2.调用foo2()之前,_data为5,进入foo2()之后再输出,已变成10,可见copy constructor的确在foo2()的参数复制时发生;
3.foo3()内有一个local object,其_data为3,把穹当做返回值输出,却变成了10,可见copy constructor的确在foo3()的返回值复制时发生;
C++ 构造函数和析构函数(Constructors & Destructors)的更多相关文章
- (C++) Interview in English. - Constructors/Destructors
Constructors/Destructors. 我们都知道,在C++中建立一个类,这个类中肯定会包括构造函数.析构函数.复制构造函数和重载赋值操作:即使在你没有明确定义的情况下,编译器也会给你生成 ...
- C++的构造函数和析构函数
1.构造函数和析构函数为什么没有返回值? 构造函数和析构函数是两个非常特殊的函数:它们没有返回值.这与返回值为void的函数显然不同,后者虽然也不返回任何值,但还可以让它做点别的事情,而构造函数和析构 ...
- C++基础学习9:构造函数和析构函数
1. 构造函数用来对类对象进行初始化,它完成对内存空间的申请.赋初值等工作. 2. 析构函数主要是用来做清理工作的. 补充:函数名或变量名前面有"::"但是没有类名,说明这是 ...
- php 的 构造函数 和 析构函数
构造函数 在C++ java里的应用及其普遍,今天好好研究了一下 php 的 构造函数 和 析构函数 构造函数 和 析构函数 构造函数 void __construct ([ mixed $args ...
- 你好,C++(33)对象生死两茫茫 6.2.3 一个对象的生与死:构造函数和析构函数
6.2.2 使用类创建对象 完成某个类的声明并且定义其成员函数之后,这个类就可以使用了.一个定义完成的类就相当于一种新的数据类型,我们可以用它来定义变量,也就是创建这个类所描述的对象,表示现实世界中 ...
- C++ 派生类构造函数和析构函数
几个问题 一个类的各数据成员的构造顺序? 按他们在类定义中出现的先后顺序:先定义者先构造. 类的对象成员的构造函数与类自身的构造函数的执行顺序? 先执行对象成员的构造函数,再执行类自身的构造函数. 构 ...
- C/C++中构造函数和析构函数能否被继承
http://bbs.csdn.net/topics/390160673 标准方面做了要求的.Even though destructors are not inherited 构造函数和析构函数是不 ...
- PHP面向对象学习-属性 类常量 类的自动加载 构造函数和析构函数 访问控制(可见性)
在类的成员方法里面,可以用 ->(对象运算符):$this->property(其中 property 是该属性名)这种方式来访问非静态属性.静态属性则是用 ::(双冒号):self::$ ...
- .NET 基础 一步步 一幕幕[面向对象之构造函数、析构函数]
构造函数.析构函数 构造函数: 语法: //无参的构造函数 [访问修饰符] 函数名() :函数名必须与类名相同. //有参的构造函数 [访问修饰符] 函数名(参数列表):函数名必须与类名相同. 作用: ...
- 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)
[源码下载] 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成 ...
随机推荐
- ABC326 A-F
vp on 2023.11.12 A.B:略. C:双指针或者二分. D:暴搜+剪枝,每一行的计算量最多是 $60$. E:期望相关.计算每个点的概率,乘上这个点的权值,并求和即可. F:meet i ...
- HTTP与WebSocket/WebDAV
WebSocket WebDAV
- MySQL创建和操纵表
表创建基础 CREATE TABLE customers ( cust_id int NOT NULL AUTO_INCREMENT , cust_name char(50) NOT NULL , c ...
- 使用OHOS SDK构建tinyexr
参照OHOS IDE和SDK的安装方法配置好开发环境. 从github下载源码. 执行如下命令: git clone https://github.com/syoyo/tinyexr.git 进入源码 ...
- 使用 OpenCV 进行文档矫正
使用 OpenCV 进行文档矫正 本文只发布于博客园与pchar博客 std::vector<std::vector<cv::Point>> cvhelper::findCor ...
- 掌握 C++ 编译过程:面试中常见问题解析
C++是一种高级编程语言,但是计算机并不能直接理解它.因此,需要将C++代码翻译成计算机可以理解的机器语言.这个过程就是编译过程,是C++程序从源代码到可执行文件的转换过程,包括预处理.编译.汇编和链 ...
- 携手HMS Core统一扫码服务, 兴业证券优理宝App提升用户扫码体验
兴业证券优理宝App联合华为HMS Core,集成HMS Core统一扫码服务,在提升扫码成功率的同时,还支持C端用户用手机端APP扫描手表端二维码,实现行情在手机与手表间跨终端流转,行情信息,抬腕可 ...
- redis 简单整理——内存的优化[二十七]
前言 简单介绍一下内存的优化. 正文 Redis所有的数据都在内存中,而内存又是非常宝贵的资源.如何优化内存的使用一直是Redis用户非常关注的问题.本节深入到Redis细节中,探索内存优化的技巧. ...
- redis 简单整理——客户端常见异常[十七]
前言 这个还是比较常见的,也就是比较对开发有用的部分. 正文 1.无法从连接池获取到连接 JedisPool中的Jedis对象个数是有限的,默认是8个.这里假设使用的默 认配置,如果有8个Jedis对 ...
- spring cloud 学习笔记 服务注册与发现(二)
前言 服务注册与发现的学习.这个其实是微服务的核心了,因为微服务的一个重要理念就是将项目拆分,达到解耦的地步.那么如何把这些服务联系到一起就很关键. 如果一个服务到另外一个服务通过ip地址之间访问,虽 ...