一、构造函数、默认构造函数

(1)、构造函数

构造函数是特殊的成员函数
创建类类型的新对象,系统自动会调用构造函数
构造函数是为了保证对象的每个数据成员都被正确初始化

函数名和类名完全相同
不能定义构造函数的类型(返回类型),也不能使用void
通常情况下构造函数应声明为公有函数,一般被隐式地调用。
构造函数被声明为私有有特殊的用途,比如单例模式,以后详谈。
构造函数可以有任意类型和任意个数的参数,一个类可以有多个构造函数(重载)

(2)、默认构造函数

不带参数的构造函数
如果程序中未声明,则系统自动产生出一个默认构造函数,是空函数

如果程序实现任何一个构造函数(包括拷贝构造函数),那么编译器将不再提供默认构造函数。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
#ifndef _TEST_H_
#define _TEST_H_

class Test
{
public:
    // 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的
    // 默认的构造函数
    Test();
    Test(int num);
    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
 
#include "Test.h"
#include <iostream>
using namespace std;

// 不带参数的构造函数称为默认构造函数
Test::Test()
{
    num_ = 0;
    cout << "Initializing Default" << endl;
}

Test::Test(int num)
{
    num_ = num;
    cout << "Initializing " << 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
11
12
13
14
15
16
17
 
#include "Test.h"

int main(void)
{
    Test t;
    t.Display();

Test t2(10);
    t2.Display();

Test *t3 = new Test(20);    // new operator
    t3->Display();

delete t3;

return 0;
}

用上面的程序测试,输出为:

可以看到构造函数是被自动调用的,且构造函数可以被重载调用;栈上的对象生存期到了会自动调用析构函数;而new
operator 做了两件事,一个是创建了对象内存,一个是调用构造函数;堆上的内存需要delete
释放,做了两件事,一是调用析构函数,二是释放内存。

//我们不能调用一个构造函数但没有提供参数(实例化对象),如
A a();
//因为是有歧义的,我们也可以看成是声明了一个没有参数的函数a,返回值是类型A的一个对象

但在函数传参的时候往往可以这样写: A()  // 即定义一个无名对象。

还有一个注意点,全局对象的构造先于main函数执行,如下:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
 
#include "Test.h"
#include <iostream>
using namespace std;

Test t(10);

int main(void)
{
    cout << "Entering main ..." << endl;
    cout << "Exiting main ..." << endl;
    return 0;
}

在return 0 时全局变量的生存期也到了,故也会自动调用析构函数。

二、析构函数

函数名和类名相似(前面多了一个字符“~”)
没有返回类型
没有参数
析构函数不能被重载
如果没有定义析构函数,编译器会自动生成一个默认析构函数,其格式如下:
类名::~默认析构函数名( )
{
}
默认析构函数是一个空函数

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
#include "Test.h"

int main(void)
{
    Test t[2] = {10, 20};

Test *t2 = new Test(2);
    delete t2;

Test *t3 = new Test[2];
    delete[] t3;

return 0;
}

注意 2] = {10, 20};
中10,20是当作参数传递给每个对象的构造函数的,如果没有对应的构造函数,比如只有2个参数的构造函数,那么编译是失败的。

实际上,构造函数和析构函数都是可以被显式调用的,只是很少这样做,可以参考这里

三、转换构造函数

单个参数的构造函数不一定是转换构造函数
将其它类型转换为类类型
类的构造函数只有一个参数是非常危险的,因为编译器可以使用这种构造函数把参数的类型隐式转换为类类型

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
#include "Test.h"

int main(void)
{
    Test t(10);     // 带一个参数的构造函数,充当的是普通构造函数的功能

t = 20;         // 将20这个整数赋值给t对象
    // 1、调用转换构造函数将20这个整数转换成类类型 (生成一个临时对象)
    // 2、将临时对象赋值给t对象(调用的是=运算符)

Test t2;

return 0;
}

可以看到初始化了一个临时对象,传递参数20,然后调用赋值运算符operator=,接着释放临时对象,最后释放的对象是已经被更改过的t 。赋值运算符的格式为:Test& Test::operator=(const Test& other);事实上如果没有自己实现,编译器也会实现一个默认的赋值运算符,做的事情跟我们现在实现的函数一样。

四、赋值与初始化的区别

在初始化语句中的等号不是运算符。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
#include "Test.h"

int main(void)
{
    Test t = 10;        // 等价于Test t(10); 这里的=不是运算符,表示初始化。

t = 20;             // 赋值操作

Test t2;
    t = t2;             // 赋值操作 t.operator=(t2);

return 0;
}

第一条语句是初始化,后面是赋值操作,参照上面临时对象的创建销毁,赋值运算符的调用可以理解输出。

五、explicit 关键字

只提供给类的构造函数使用的关键字。
编译器不会把声明为explicit的构造函数用于隐式转换,它只能在程序代码中显示创建对象

假设在Test 类的构造函数Test(int num); 前面加上explicit 关键字,那么Test t = 10; 或者 t = 20; 这种语句都是编译不通过的,因为不允许隐式转换。

参考:

C++ primer 第四版
Effective C++ 3rd
C++编程规范

构造函数、析构函数、赋值与初始化、explicit关键字的更多相关文章

  1. 21.C++- "++"操作符重载、隐式转换之explicit关键字、类的类型转换函数

    ++操作符重载 ++操作符分为前置++和后置++,比如: ++a;  a++; ++操作符可以进行全局函数或成员函数重载 重载前置++操作符不需要参数 重载后置++操作符需要一个int类型的占位参数 ...

  2. C++中构造函数,拷贝构造函数和赋值函数的区别和实现

    C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法.下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象 ...

  3. C++中的构造函数,拷贝构造函数,赋值函数

    C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法.下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象 ...

  4. 从零开始学C++之构造函数与析构函数(一):构造函数、析构函数、赋值与初始化、explicit关键字

    一.构造函数.默认构造函数 (1).构造函数 构造函数是特殊的成员函数 创建类类型的新对象,系统自动会调用构造函数 构造函数是为了保证对象的每个数据成员都被正确初始化 函数名和类名完全相同 不能定义构 ...

  5. C++11六大函数(构造函数,移动构造函数,移动赋值操作符,复制构造函数,赋值操作符,析构函数)

    在C++中,有三大函数复制控制(复制构造函数,赋值操作符,析构函数),而在C++11中,加入了移动构造函数,移动赋值操作符.我就斗胆将他们命名为六大函数好了. 一.构造函数 c++primer中说过: ...

  6. c++类大四个默认函数-构造函数 析构函数 拷贝构造函数 赋值构造函数

    每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数).对于任意一个类A,如果不编写上述函数,C++编译器将自动为A 产生四个缺省的函数,例如: A ...

  7. C++ 构造函数放置默认转换explicit关键字(2)

    按照默认规定,只有一个参数的构造函数也定义了一个隐式转换,将该构造函数对应数据类型的数据转换为该类对象,如下面所示: class String { String ( const char* p );  ...

  8. C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容

    一.本文目的与说明 1. 本文目的:理清在各种继承时,构造函数.复制构造函数.赋值操作符.析构函数的执行顺序和执行内容. 2. 说明:虽然复制构造函数属于构造函数的一种,有共同的地方,但是也具有一定的 ...

  9. C++的拷贝构造函数、operator=运算符重载,深拷贝和浅拷贝、explicit关键字

    原文地址:https://blog.csdn.net/shine_journey/article/details/53081523 1.在C++编码过程中,类的创建十分频繁. 简单的功能,当然不用考虑 ...

随机推荐

  1. require(): open_basedir restriction in effect. 解决方法

    在linux服务器部署thinkphp5的时候PHP报了这个错误, 如下: Warning: require(): open_basedir restriction in effect. File(/ ...

  2. React事件初探

    作者:朱灵子 React 是一个 Facebook 和 Instagram 用来创建用户界面的 JavaScript 库.创造 React 是为了解决一个问题:构建随着时间数据不断变化的大规模应用程序 ...

  3. uva 1203 - Argus(优先队列)

    option=com_onlinejudge&Itemid=8&page=show_problem&problem=3644" target="_blank ...

  4. 关于目前自己iOS项目使用的插件

    1. VVDocumenter-Xcode 规范注释生成器. 2. RTImageAssets 本项目是一个 Xcode 插件,用来生成 @3x 的图片资源对应的 @2x 和 @1x 版本,只要拖拽高 ...

  5. Combogrid的onChange和onSelect

    这里只作记录一下 在easyui开发过程中遇到下边两个事儿 combogrid/combotree做级联: $("#" + deptId).combotree( { url : u ...

  6. 《实战突击:PHP项目开发案例整合(第2版)(含DVD光盘1张)》

    <实战突击:PHP项目开发案例整合(第2版)(含DVD光盘1张)> 基本信息 作者: 徐康明    辛洪郁 出版社:电子工业出版社 ISBN:9787121221378 上架时间:2014 ...

  7. OpenCV学习(10) 图像的腐蚀与膨胀(1)

    建议大家看看网络视频教程:http://www.opencvchina.com/thread-886-1-1.html    腐蚀与膨胀都是针对灰度图的形态学操作,比如下面的一副16*16的灰度图. ...

  8. 自建一个Java Spring MVC项目

    用IDEA Intellij,本来创建的是SpringMVC项目,但是下载的时候,太慢了.所以还是用的Maven项目. 选择Maven 项目->Archetype->Web applica ...

  9. JavaScript中的递归函数问题

    学过其它编程语言的都应该会知道递归这个问题,递归函数是在一个函数通过名字调用自身的情况下后构成的. function fac(num){ if(num<=1){ return 1; }else{ ...

  10. http响应报文,如果响应的内容比较大,客户端怎么样判断接收完了呢?

    1.   http协议有正文大小说明的content-length 2. 或者分块传输chunked的话  读到0\r\n\r\n 就是读完了 ---------------------------- ...