C++: 构造函数 & 拷贝构造函数 & 析构函数 & explicit

一、构造函数

1.定义

构造函数是类的成员函数,函数名与类名相同,在创建类类型对象时,由编译器自动调用,一个对象在被创建时,会调用一次构造函数,此后,不会在调用构造函数,即就是构造函数是在对象的整个生命周期里只会被调用一次

2.构造函数的书写格式

这里写图片描述

3.构造函数的特性

(1)构造函数的函数名与类名相同;
(2)构造函数有初始化列表;
(3)构造函数由编译器自动调用;
(4)构造函数的调用时机:

在对象创建时才会被调用,并且在对象的整个生命周期中只存在一次;

(5)构造函数没有返回值,但是有参数;

ps:其实构造函数是有返回值的,返回的是当前对象的地址

(6)构造函数没有显式定义时,编译器会提供一个默认的构造函数

ps:编译器合成默认构造函数有四个场景,具体参考博客:

(7)构造函数不能用const修饰;

const在修饰函数时,实际是在修饰隐含的this指针,表示在此函数中不可以对类的任何成员进行修改,而构函数恰恰是用来构造对象的,所以构造函数不能用const修饰;

(8)构造函数可以重载,实参决定了调用那个构造函数;
(9)无参构造函数和带有缺省值(此处是全缺省)的构造函数都认为是缺省构造函数,并且缺省的构造函数只能有一个,即就是无参的构造函数和带有缺省值的构造函数只能存在一个;
(10)构造函数不能为虚函数

ps:无参构造函数

class Stu
{
public:
    Stu()
    {}
private:
    int _id;
    int _grade;
    char _name[5];
};

带有缺省值的构造函数

这里写图片描述

4.构造函数的作用

(1)初始化对象;
(2)构建对象;
(3)类型转换(此时的构造函数一定要是单参的,也可以是多个参数,只要带着缺省值就好)

5.初始化列表

(1)初始化列表的格式

以一个冒号开始,接着是一个逗号分隔的数据成员列表,每个数据后面圆括号中的值就是该变量的初始值,如上图 ,就是一个初始化列表

(2)作用

是用来初始化对象的

(3)特性

a.每个数据成员在初始化列表里只能出现一次;
b.初始化列表只是用来初始化成员的,并不指定这些成员的初始化顺序,数据成员在类中的定义顺序才是参数列表的初始化顺序
ps:在初始化变量时,尽量与变量的声明顺序一致,避免造成不必要的错误

(4)类中以下成员必须要在初始化列表中定义:

a、引用数据成员
因为引用类型的变量必须初始化,而如果在函数体中才开始初始化的话,此时就是赋值,不是初始化了,所以对引用成员必须放在初始化列表中初始化
b、const修饰的数据成员
c、类类型数据成员

6.关键字explicit

(1)用此关键字修饰构造函数的话,会抑制由构造函数定义的隐式转换
(2)explicit使用位置
当创建类成员变量时,加上此关键字就可以了,在类外就不需要在重复了

二、拷贝构造函数

1.定义

此函数只有单个形参,而且该参数的类型是对本类类型的引用(常用const修饰)
ps:拷贝构造函数是特殊的构造函数,创建对象时使用已经存在的同类对象来进行初始化

2.拷贝构造函数的格式

这里写图片描述

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Stu
{
public:
    Stu(int id=10, int grade=100,char*name="张三")
        :_id(id)
        , _grade(grade)
    {
        strcpy(_name, name);
        cout << _id << "," << _grade << "," << _name << endl;
    }
    Stu(const Stu&s)
    {
        _id = s._id;
        _grade = s._grade;
        strcpy(_name, s._name);
    }
private:
    int _id;
    int _grade;
    char _name[5];
};
int main()
{
    Stu s1(1, 99,"李四");
    Stu s2(2);
    Stu s3;
    Stu s4(s1);
    system("pause");
    return 0;
}

3.拷贝构造函数的调用时机

在构建对象时,由编译器自动调用

4.拷贝构造函数的参数类型为什么是引用?

这里写图片描述
ps:如果不传引用,程序会崩溃,导致栈溢出

5.拷贝构造函数的特性

(1)由编译器自动调用;
(2)是构造函数的重载,所以拷贝构造函数的函数名与类名相同;
(3)只有单个形参,且此参数是类类型的引用,常用const修饰;
(4)构造函数的特性,拷贝构造函数都有,只有一个特性拷贝构造函数是没有的,就是构造函数可以重载,拷贝构造函数可以重载;
(5)拷贝构造函数不能重载;
(6)如果没有显式定义,系统会自动合成一个默认的拷贝构造暗黑少女胡,合成的拷贝构造函数会依次拷贝类的数据成员,完成相关对象的初始化;
ps:系统合成默认的拷贝构造函数是有具体的场景的(场景与系统自动合成默认构造函数的场景类似),并不是什么时候都会合成。

6.使用场景

(1)对象实例化对象

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Stu
{
public:
    Stu(int id=10, int grade=100,char*name="张三")
        :_id(id)
        , _grade(grade)
    {
        strcpy(_name, name);
        cout << _id << "," << _grade << "," << _name << endl;
    }
    Stu(const Stu&s)
    {
        _id = s._id;
        _grade = s._grade;
        strcpy(_name, s._name);
        cout << _id << "," << _grade << "," << _name << endl;
    }
private:
    int _id;
    int _grade;
    char _name[5];
};
int main()
{
    Stu s1(1, 99,"李四");
    Stu s2;
    Stu s3(s2);
    system("pause");
    return 0;
}

运行结果:这里写图片描述

(2)传值方式作为函数的参数

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Stu
{
public:
    Stu(int id=10, int grade=100,char*name="张三")
        :_id(id)
        , _grade(grade)
    {
        strcpy(_name, name);
        cout << _id << "," << _grade << "," << _name << endl;
    }
    Stu(const Stu&s)
    {
        _id = s._id;
        _grade = s._grade;
        strcpy(_name, s._name);
    }
    void SetInfo(const Stu s){
    _id = s._id;
    _grade = s._grade;
    strcpy(_name, s._name);
    cout << _id << "," << _grade << "," << _name << endl;

    }
private:
    int _id;
    int _grade;
    char _name[5];
};
int main()
{
    Stu s1(1, 99,"李四");
    Stu s2;
    Stu s3(s2);
    Stu s4;
    s4.SetInfo(s1);
    system("pause");
    return 0;
}

运行结果:这里写图片描述
(3)传值方式作为函数的返回值

三、析构函数

1.定义

与构造函数相反,析构函数是用来销毁对象的

2.析构函数的调用时机

在对象被销毁时,由编译器自动调用

3.析构函数的作用

销毁对象,完成类的一些清理和汕尾工作

4.析构函数的格式

析构函数名与构造函数名相同,只不过在函数名之前多加了一个符号:~

~Stu()
{
  需要销毁的类的成员
}

5.析构函数的特性

(1)析构函数名=在类名之前多加一个字符:~
(2)析构函数无参数无返回值
(3)在对象生命周期结束时,析构函数由编译器自动调用
(4)一个类只能有一个析构函数,若析构函数没有显式定义,系统会自动合成一个缺省的析构函数

ps:只有当涉及到资源的清理的时候,系统才会合成一个析构函数
(5)析构函数不能被重载,因为析构函数没有参数
(6)析构函数不是删除对象,而是做一些资源清理工作

编译器合成构造函数的情况:https://blog.csdn.net/dangzhangjing97/article/details/78597264

猜你喜欢

转载自blog.csdn.net/dangzhangjing97/article/details/80794102