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

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

(1)、构造函数

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

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

(2)、默认构造函数

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

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

#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_
#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函数执行,如下:

#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 时全局变量的生存期也到了,故也会自动调用析构函数。

二、析构函数

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

#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;
}

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

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

三、转换构造函数

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

#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);事实上如果没有自己实现,编译器也会实现一个默认的赋值运算符,做的事情跟我们现在实现的函数一样。

四、赋值与初始化的区别

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

#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++编程规范

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java 源码分析

静态内部类

定义:静态内部类,定义在类中,任何方法外,用static定义;静态内部类只能访问外部类的静态成员。 注意点: 一般情况下,如果一个内部类不是被定义成静态内部类,...

2948
来自专栏编程

C语言编程精华——《指针》

C语言编程精华--《指针》 使用指针有两方面的优势: 1、用指针能够更好完成某些任务; 2、有些任务只能是用指针才能够完成。 一、指针定义 1、计算机的内存 计...

1680
来自专栏编程

Python之匿名函数

Python之匿名函数 今天给大家介绍一下Python中的匿名函数。 匿名函数 由关键字来定义 参数列表不需要小括号 冒号不是开启新的语句块的 只能写在一行上 ...

17610
来自专栏机器学习算法与Python学习

python: 函数详解

def square(x): return x**2 >>> square <function square at 0x031AA230> >>> dir(sq...

3927
来自专栏待你如初见

Day05

832
来自专栏运维技术迷

PHP-函数

创建函数的语法 //function创建函数的关键字 function functionName() { 要执行的代码; } 函数准则 函数的名称应该显示出他的...

3007
来自专栏炉边夜话

构造函数浅析

构造函数浅析<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" ...

713
来自专栏子勰随笔

Java参数引用传递引发的惨案(又一次Java的String的“非对象”特性的踩坑经历)

1856
来自专栏python读书笔记

python数据分析基础day4-字典字典的定义字典创建字典元素的获取字典的排序

今天说一下重要的数据类型,字典。 字典的定义 python中字典类型就是键值对的集合,其中键在一个字典中必须是唯一的,值没有这个要求。此外,值可以是数值,字符串...

2747
来自专栏C语言及其他语言

C语句和程序流

1、 表达式和语句 在C中,表达式代表值,而语句代表给计算机的指令。 表达式 表达式由运算符和操作数组成。最简单的表达式只是一个不带运算符的常量或者变...

3106

扫码关注云+社区