【C++】类和对象(3)(默认成员函数--拷贝构造&赋值重载)

引言

前文介绍了C++中默认成员函数中的构造函数和析构函数,相信已经对它们的功能与用法有了基本认识,本文接着介绍也很常见的拷贝构造函数和赋值重载函数,便于对C++进一步的学习。

拷贝构造函数

补充知识:深浅拷贝

深拷贝和浅拷贝是C++中对象拷贝的两种不同方式。
浅拷贝是指将一个对象的数据成员的值复制到另一个对象中,这样两个对象将共享相同的数据。当其中一个对象修改了数据,另一个对象也会受到影响。
深拷贝是指创建一个新的对象,并将原对象的数据成员的值复制到新对象中。新对象和原对象是完全独立的,互不影响。

拷贝构造函数:

引子

在现实世界里,我们有时候会看到双胞胎,他们通常外貌很相似,那么类比到创建对象时,如果已经存在一个创建好的类对象时,能不能用它创建一个一模一样的新对象呢。答案是可以的,并且在C++中专门设定了一个函数来完成这个功能,也就是构造拷贝函数。

定义

拷贝构造函数是构造函数的重载,用于创建一个对象时,以另一个同类型对象作为参数,从而将参数对象的数据成员的值复制给新创建的对象。函数只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰)。

使用注意

1.拷贝构造函数的参数只有一个且必须是类类型对象的引用,如果使用传值方式,编译器会直接报错,因为C++规定自定义类型的传值传参会自动调用拷贝构造函数,此时如果传值传参会引发无穷的递归调用。

class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// Date(const Date d) //错误写法:编译错误,引发无穷递归
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);
return 0;
}

递归调用过程如下图所示:在这里插入图片描述
2.未显式定义拷贝构造函数,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,也就是浅拷贝。
所以当类中资源没有涉及到资源申请时,系统默认的拷贝构造函数可以完成拷贝,但是当涉及到资源申请时,需要自己编写拷贝构造函数,因为浅拷贝不能满足要求。

比如说一个类对象s1在堆上申请开辟空间,此时如果用s1默认拷贝构造函数拷贝构造一个新对象s2,此时s1和s2都指向同一块内存空间,当程序退出,s1和s2都要销毁,s2先析构,将该空间释放,但当s1析构时,也会释放该空间,造成堆上同一块内存空间的多次释放,从而使程序崩溃。

3.常见调用场景:
使用已存在对象创建新对象
函数参数类型为类类型对象
函数返回值类型为类类型对象

 class Date
{
public:
 Date(int year, int minute, int day)
 {
 cout << "Date(int,int,int):" << this << endl;
 }
 Date(const Date& d)
 {
 cout << "Date(const Date& d):" << this << endl;
 }
 ~Date()
 {
 cout << "~Date():" << this << endl;
 }
private:
 int _year;
 int _month;
 int _day;
};
Date Test(Date d)
{
 Date tmp(d);
 return tmp;
}
int main()
{
 Date d1(2024,10,8);
 Test(d1);
 return 0;
}

运行结果:

Date(int,int,int):006FFC74
Date(const Date& d):006FFB64
Date(const Date& d):006FFB94
~Date():006FFB64
~Date():006FFB94
~Date():006FFC74

编译器在优化后显示三次构造,三次析构,其实是有4次构造和4次析构:
1.调用构造函数创建d1;
2. Test函数值传递,传参时调用拷贝构造创建d;
3. 调用拷贝函数创建tmp;
4. Test函数以值的方式返回,返回时用tmp拷贝构造临时对象用来返回。

所以为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用就尽量使用引用。

赋值运算符重载函数

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表。

运算符重载:

返回值类型 operator操作符(参数列表)

bool operator==(Date& d1,Date& d2)

注意:

有5个运算符不能重载

.*
::
sizeof
.
?:

重载操作符必须有一个类类型参数,作为类成员函数重载时,第一个参数为隐藏的this,用于内置类型的运算符不可以改变其含义。

赋值运算符重载

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;
   }
 //
 Date& operator=(const Date& d)
 {
 if(this != &d)
       {
            _year = d._year;
            _month = d._month;
            _day = d._day;
       }
        
        return *this;
 }
 private:
 int _year;
 int _month;
 int _day;
 };

注意:

1.重载格式:
参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
返回*this :可以连续赋值

2.赋值重载函数只能重载成类的成员函数,不可以重载成全局函数

因为赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了。

3.与拷贝构造类似,用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。内置类型值拷贝,自定义类型调用对应的赋值重载。
同样的,和拷贝构造一样的情况,如果类中涉及到资源的管理,不显示定义,系统默认的赋值运算符重载函数因为只能浅拷贝,可能会造成内存的重复释放,此时必须自己手动实现赋值重载。

前置++和后置++重载

C++为了区分前置++和后置++, 规定后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器会自动传递。

class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 // 前置++:返回+1之后的结果
 // this指向的对象函数结束后不会销毁,可以用引用方式返回提高效率
 Date& operator++()
 {
 _day += 1;
 return *this;
 }

 // 后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1
 // 而temp是临时对象,因此只能以值的方式返回,不能返回引用
 Date operator++(int)
 {
 Date temp(*this);
 _day += 1;
 return temp;
 }
private:
 int _year;
 int _month;
 int _day;
};

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

const成员函数

const修饰的成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

class Date{
public:
//void Print(const Date* this)
void Print() const
{
cout << “year:” << _year << endl;
cout << “month:” << _month << endl;
cout << “day:” << _day << endl << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
const Date d1(2024,10,8);
d1.Print();
}

注意:const对象只能调用const成员函数。const成员函数内不可以调用其他的非const成员函数。

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

这两个成员函数一般用编译器默认生成的就可以了,除非想修改返回的内容。

class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&() const
{
return this ;
}
private :
int _year ;
int _month ;
int _day ;
};

本文到此就结束了,本文主要就C++的拷贝构造函数和运算符重载函数展开叙述,结合上篇文章较为完整地总结了C++的默认成员函数,希望能带给你帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值