C++中类的默认成员函数总结

1.构造函数

概念:构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每一个成员都有一个合适的初始值,并且在对象的生命周期内只调用一次
注意:构造函数的任务是初始化对象,而不是开空间创建对象,但是类中的数据成员不能在声明类的时候初始化,因为类不是一个实体,而是一种特殊的数据类型,不占据存储空间

特征
1.函数名与类名相同
2.无返回值
3.对象实例化编译器自动调用对应的构造函数
4.构造函数可以重载
5.如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,显式定
义之后编译器将不再生成
6.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意无参构造函数,全缺省构造函数,编译器默认生成的构造,都可认为是默认成员函数

class Date
{
public:
	// 无参构造函数
	Date()
	{}
	// 带参构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
void TestDate()
{
	Date d1; // 调用无参构造函数,无参的构造函数没有初始化,为随机值
	Date d2(2020, 9, 27); // 调用带参的构造函数
	Date d3();	 // 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明,						
}

构造顺序
1.全局对象忧先于局部对象进行构造
2.静态对象优先于普通对象进行构造
3.静态局部对象,走到其所在的内一行才会初始化
4.全局的对象和全局的静态对象差别是链接属性

初始化列表
以一个冒号开始,接着以一个逗号分隔数据成员列表,每个成员变量后面跟着一个放在括号中的初始值或表达式(构造函数体内的语句只能将其称作赋初值,不能称作初始化),在函数体内任何代码执行之前完成初始化工作。


class Date
{
public:
	Date(int year, int month, int day)
		: _year(year)    //括号中为初始值或表达式
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};

注意
1.每个成员变量只能在初始化列表中出现一次(即只能初始化一次)
2.当类中包含以下成员时,必须放在初始化列表位置进行初始化
引用成员变量,const成员变量,自定义类型成员变量(该类没有默认构造函数)
3.尽量使用初始化列表初始化,对于自定义类型,会优先使用初始化列表初始化
4.成员变量在类中的声明次序就是在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
5.类的数据成员初始化可以采用初始化列表和函数体内赋值两种方式,但是使用初始化列表的方式效率更高

2.析构函数

概念:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象的销毁是编译器完成的。对象在销毁时(生命周期到了之后)会自动调用析构函数,完成类的资源清理,如关闭打开的文件,释放开辟过的内存等工作

特征
1.析构函数名是在类名前加上字符~(取反符号)
2.无参数无返回值(不能重载)
3.一个类有且只有一个析构函数,如果没有显式定义,编译系统会自动生成默认的析构函数
4.对象生命周期结束时,C++编译系统自动调用析构函数
5.同一文件下全局变量和全局静态对象看定义顺序,后定义的先析构

class Array
{
public:
    Array(int size)
    {
        _ptr = (int*)malloc(size*sizeof(int));
    }
    ~Array()
    {
        if (_ptr)
        {
            free(_ptr);//释放堆上内存
            _ptr = NULL;
        }
    }
private:
    int* _ptr;
};

3.拷贝构造函数

概念:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在创建对象时使用同类对象进行初始化时调用的构造函数就是拷贝构造函数。
特征
1.拷贝构造函数是构造函数的一个重载形式
2.拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归引用
3.若未显式定义,系统生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝是浅拷贝或者值拷贝(浅拷贝:将对象按一个字节一个字节拷贝过去)
4.编译器生成的默认拷贝构造函数


class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)	//缺省构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)		//拷贝构造函数
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(d1);	//调用拷贝构造函数
	Date d3 = d1;	//调用拷贝构造函数
	return 0;
}

为什么拷贝构造函数的参数使用传值方式会引起无穷递归调用?
拿上面的日期类来说,当我们想将d2使用同类的d1初始化时即“Date d2(d1)”,此时d1作为参数传到拷贝构造函数Date (const Date date)中,此时因为传值所以又引发对象的拷贝即 Date date(d1),然后继续传值引发对象的拷贝,所以引发了无穷递归。
在这里插入图片描述

4.赋值运算符重载

在对同类之间的对象相互赋值(对象中所有数据成员)的时候,此时的赋值操作是通过赋值运算符“=”重载实现的。

class Date
{
public :
Date(int year = 2020, int month = 9, int day = 27)
{
    _year = year;
    _month = month;
    _day = day;
}
Date (const Date& d)	
{
    _year = d._year;
    _month = d._month;
    _day = d._day;
}
Date& operator=(const Date& d)	//赋值运算符重载
{
		if (this != &d)
        {
            this->_year = d._year;
            this->_month = d._month;
            this->_day = d._day;
        }
        return *this;
}
}
private:
    int _year ;
    int _month ;
    int _day ;
};

注意
1.参数类型
2.返回值
3.检测是否自己给自己赋值
4.返回*this
5.一个类如果没有显式定义赋值运算符重载,编译器会自动生成一个,完成对象按字节序的值拷贝

5.取地址和const取地址操作符重载

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可

class Date
{
public:
	Date* operator&()	//取地址操作符重载
	{
		return this;
	}
	const Date* operator&()const	//const取地址操作符重载
	{
		return this;
	}
private:
	int _year; 
	int _month; 
	int _day; 
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值