当前位置 博文首页 > 文章内容

    C++类中的六大默认成员函数详解

    作者:shunshunshun18 栏目:未分类 时间:2021-04-13 14:43:37

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    在C++中,当你去创建一个类的时候,即便这个类是空类,也会自动生成下面6个默认成员函数,在本篇博客中,我将逐一分析下面6个默认成员函数。

    构造函数

    构造函数并不是去构造函数的函数,而是去对函数进行初始化的函数。构造函数的函数名与类名相同,当我们每次创建类对象的时候,就会自动调用构造函数。构造函数在对象的生命周期中只会调用1次。

    class Date
    {
    public:
        //构造函数
    	Date(int year = 2021, int month = 4, int day = 11)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };

    构造函数的几个特点

    ①函数名与类名相同

    ②无返回值

    ③对象实例化时编译器自动调用对应的构造函数

    ④构造函数可以重载

    class Date
    {
    public:
        //构造函数的重载:
        //无参的构造函数
    	Date()
    	{}
        //需要传参的构造函数
    	Date(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };

    ⑤如果类中没有显式定义构造函数(就是自己没有去定义构造函数),那么编译器会自动生成一个无参的默认构造函数;

    如果类中显式定义了构造函数,那么编译器将不再生成,而是去使用用户定义的构造函数。

    ⑥默认构造函数只能同时存在1个。默认构造函数分为以下3种:①无参的构造函数 ②全缺省的构造函数 ③编译器默认生成的构造函数

    默认构造函数的共同特点是:不用传参就可以调用

    class Date
    {
    public:
    	//下面2种和 当你不写构造函数时编译器自动生成的默认构造函数只能同时存在1种
        //无参的
    	Date()
    	{
    		_year = 2021;
    		_month = 4;
    		_day = 11;
    	}
        //全缺省的
    	Date(int year = 2021, int month = 4, int day = 11)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };

    ⑦编译器生成的默认的构造函数,对内置类型(int, char, double...)不会做任何处理,但是会针对自定义类型的成员,调用它的构造函数去进行初始

    构造函数调用的2种写法:

    int main()
    {
        //无参时
        Date d;
        //单个参数
    	Date(1);
    	Date d1 = 2;//这种写法会发生隐式类型转换
        //多个参数
        Date(2021, 4, 11);
        Date d2 = {2021, 4, 11};//C++11中才支持的写法
    }

    构造函数与初始化列表

    初始化列表:以冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。

    初始化列表有什么用?

    初始化列表,顾名思义就是对对象进行初始化的,但是我们已经可以在构造函数体内进行初始化了(通过对成员变量进行赋值来进行初始化),为什么还需要初始化列表?

    这是因为,有些类型的数据无法通过在构造函数体内进行赋值来进行初始化。这样的数据类型有下面3种:

    1. 引用成员变量
    2. const成员变量
    3. 自定义类型成员 (且它的类没有默认构造函数[即,它必须要进行传参])

    上面的三种数据类型有一个共同的特点,它们都要求你在定义变量的时候进行赋值。

    比如,引用成员变量,使用引用的时候必须进行初始化,否则语法就是错误的。

    析构函数

    析构函数的作用与构造函数相反,在对象的生命周期结束的时候会自动调用析构函数,完成类的一些资源清理的工作。

    析构函数的特点

    • 析构函数名是在类名的前面加上~
    • 无参,无返回值
    • 一个类中有且只有1个析构函数。如果未显式定义,系统会自动生成默认的析构函数。(如果定义了,则采用显式定义的)
    • 对象生命周期结束时,C++编译系统会自动调用析构函数
    • 编译器生成的默认的析构函数,对内置类型(int, char, double...)不会做任何处理,但是会针对自定义类型的成员,会去调用它的析构函数

    析构函数的一般使用情况:

    一般使用在那些涉及到动态内存开辟空间的类中,因为这样的对象需要对其动态开辟的空间进行释放。

    class Stack
    {
    public:
        //构造函数
    	Stack(int n = 3)
    	{
    		_a = (int*)malloc(sizeof(int)*n);
    		_size = 0;
    		_capacity = n;
    	}
        //析构函数
    	~Stack()
    	{
    		free(_a);
    		_size = _capacity = 0;
    	}
    private:
    	int* _a;
    	int _size;
    	int _capacity;
    };

    拷贝构造函数

    拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象去创建新的对象时,编译器会自动调用拷贝构造函数。

    拷贝构造函数的特点

    • 拷贝构造函数是构造函数的一个重载
    • 拷贝构造函数的参数只有1个,且必须使用引用传参,如果使用引用传值的形式会引发无限递归

    拷贝构造函数的2种调用方法(完全等价的):

    int main()
    {
    	Date d1(1);
    	//拷贝构造函数
    	Date d2(d1);  //1
    	Date d3 = d1; //2
     
    	return 0;
    }

    赋值运算符重载

    在了解赋值运算符重载之前,我们需要先知道什么是运算符重载。

    运算符重载

    运算符重载是具有特殊函数名的函数。

    函数名:关键字operator后面接需要重载的运算符符号(如:operator>)

    函数原型:返回类型 operator操作符 (参数列表)

    注意:

    • operator后面必须跟着的是操作符(这样是不可以的 operator@)
    • 重载操作符必须有一个类类型或者枚举类型的操作数
    • 用于内置类型的操作符,其含义无法改变。(比如内中的整型+,3+5这其中的+的意义不会改变)
    • this指针为限定的第一个形参,也就是this作为第一个操作数
    • .*、::、sizeof、?:、. 这5个操作符无法进行重载。

    赋值运算符重载

    class Date
    {
    public:
    	Date(int year = 2021, int month = 4, int day = 11)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	//赋值运算符重载
    	Date& operator=(const Date& d)
    	{
    		_year = d._day;
    		_month = d._month;
    		_day = d._day;
    		return *this;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };

    注意:赋值运算符重载必须有返回值,如果没有返回值的话,无法解决 a = b = c 这种连续赋值的操作。

    拷贝构造函数与赋值运算符重载

    Date d1(1);
    	Date d2(0);
    	
    	//赋值运算符重载
    	d2 = d1;     //注意,只有2个操作数都是已经定义过的变量时,才会调用赋值运算符重载
     
    	//拷贝构造函数
    	Date d3(d1);
    

    浅拷贝

    浅拷贝是你在没有写拷贝构造函数和operator=时,编译器自动调用的默认成员函数。它的功能是将对象以字节的为单位拷贝过去。

    取地址与const取地址操作符重载

    这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可(编译器默认的基本就够用了),只有特殊情况,才需要重载,比如想让别人获取到指定的内容。

    class Date
    {
    public:
        //取地址操作符重载
    	Date* operator&()
    	{
    		return this;
    	}
        //const取地址操作符重载
    	const Date* operator&()const
    	{
    		return this;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    class Date
    {
    public:
        //初始化列表
    	Date(int year, int month, int day)
    		:_year(year),
    		 _month(month),
    		 _day(day)
    	{}
     
    private:
    	int _year;
    	int _month;
    	int _day;
    };