C++基础之四个默认函数(构造,析构,拷贝构造,赋值运算符重载)

一、构造函数

  类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。构造函数的名称与类的名称是完全相同的并且不会返回任何类型,也不会返回 void。构造函数用于为某些成员变量设置初始值

构造函数的默认值

设置规则:

  1. C++类的构造函数若只提供一个默认值则只能对最右边的参数提供默认值。
  2. 既可以在构造函数的声明中,也可以在构造函数的实现中提供默认值,不能在两者同时提供默认值

赋值方法

  1. 直接在大括号中进行赋值,这个就不细说了注意如果是指针需要先开辟内存。
  2. 成员初始化列表中赋值,一般我们建议使用成员初始化列表进行赋值。

首先,我们需要明白类的构造顺序

  1. 初始化阶段。分配内存,调用构造函数时,隐式/显示的初始化各数据成员(构造函数列表的初始化方式不是按照列表的的顺序,而是按照变量声明的顺序同时初始化显隐数据成员)

  2. 计算阶段。进入构造函数后在构造函数中执行一般赋值与计算(也就是我们说的大括号赋值)。

《C++ Primer》一书中提到在以下三种情况下需要使用初始化成员列表:

  • 需要初始化的数据成员是对象的情况(这里包含了继承情况下,通过显示调用父类的构造函数对父类数据成员进行初始化);

  • 需要初始化const修饰的类成员或初始化引用成员数据;

  • 子类初始化父类的私有成员;


还有一个原因是,成员初始化列表对于内置类型来讲,效率上没有太大的变化,但是对于类类型来讲,可以提高程序的执行效率如果类类型若不在成员初始化列表中初始化,那么就需要多一次构造函数的调用,根本区别就在拷贝构造和赋值的区别,拷贝构造一次完成,赋值需要两次完成。

详细解释可参考:初始化列表的应用场景

二、析构函数

  与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如 ~stud( ),以区别于构造函数。它不能带任何参数也没有返回值(包括void类型)只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。

三、拷贝构造函数

C++拷贝构造函数的理解

四、赋值运算符重载函数

  当我们编写一个类的时候,,并不需要为该类重载“=”运算符,因为编译系统为每个类提供了默认的赋值运算符“=”,使用这个默认的赋值运算符操作类对象时,该运算符会把这个类的所有数据成员都进行一次赋值操作。但是默认的赋值运算符重载函数只是进行了浅拷贝(只是进行值的复制),当我们遇到需要开辟空间的成员变量时就需要进行深拷贝,而编译器并不会提供,所以需要我们自己来写一个赋值运算符重载函数。


作用:拿一个已存在的对象来给另一个已存在的对象赋值

编写步骤:

  1. 判断目标赋值对象是否是源对象,如果是直接返回*this;
  2. 释放开辟过的成员变量内存;
  3. 如果有成员变量需要开辟内存则进行内存开辟;
  4. 进行赋值操作。

代码示例:

CGoods& operator=(const CGoods& rhs)
{
	if(this == &rhs)//如果相等便是相同对象
	{
		return *this;
	}
	delete []mname;
	mname = new char[strlen(rhs.mname) + 1];
	strcpy(mname,rhs.mname);
	mprice = rhs.mprice;
	mamount = rhs.mamount;
	return *this;
}

注意事项:
  我们可以发现参数加了引用和const关键字,当然这两个都不是必须的。但是我们一般建议这样写,理由如下:

  • 例如我们生成了两个对象good1,good2此时需要将good1的值赋good2即good2 = good1,使用赋值运算符重载函数的原型即为good2.operator=(good1),其中good1赋值给了rhs,如果我们不使用引用则会再次调用一次构造函数生成类对象,当对象中成员较多时,无疑加大了内存消耗而参数加引用可以防止多生产一次不必要的对象。
  • 为何要加const关键字?
    首先我们引入一个概念:
  1. 内置类型可以产生临时量为常量在寄存器中。
  2. 自定义类型产生的临时量为变量在内存中。
    例如我们有如下的示例代码:
class CGoods 
{
public:
	CGoods(char* name)
	{
		mname = new char[strlen(name)+1];
		strcpy(mname,name);	
	}
private:
	char* mname;
	int mprice;
	int mamount;
}
int main()
{
	CGoods good = "helloworld";
	return 0;
}

  我们可以发现上述代码存在一个带参的构造函数,则CGoods good = “helloworld”;这一行中helloworld会产生一个临时对象由于helloworld是char*类型在上面第2个概念我们可以知道会将一个常量传给运算符重载函数的rhs,那么此时必须要加上const关键字,因为我们要防止常量内存快被修改的风险,const关键字用法具体可以参照我的另一篇博客c和c++conset的用法和区别,所以加const关键字主要是为了:1.防止实参被修改。2.杜绝内置类型临时对象的生成修改常量内存块的风险。

  通常隐式生成的临时量为一个常量,隐式(编译器推演good3 =”good3”;),显示(明确的告诉编译器生成什么样的临时量,good3 = Cgoods(“good3”);)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值