前言:
上一篇文章,我们了解了类的默认成员函数——构造函数、析构函数、拷贝构造函数,这篇我们让我们接着了解。
一、赋值运算符重载:
1.运算符重载:
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也就是说它跟普通的函数一样具有返回值类型,安徽念书名字以及参数列表。
函数名字:关键字(operator)后面接需要重载的运算符符号;
函数原型:返回值类型operator操作符(参数列表)。
ps:
①.只能在原有的运算符基础上重载,不能通过连接其他符号来创建新的操作符,比如:operator@、operator#等等都不行;
②.重载操作符必须有一个类类型参数;
③用于内置类型的运算符,其含义不能改变,例如:内置的整形+,不能改变其含义;
④作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this指针。
⑤ *,::,sizeof,?: , . 这五个操作符不能重载。
class Date
{
public:Date(int year=2023,int month=9,int day=15){_year=year;_month=month;_day=day;}
public:
//这里因为运算符重载成全局的需要成员函数是公有的,后期可以用友元解决,或者重载成成员函数;int _year;int _month;int _day;
};
bool opprator==(const Date& d1,const Date& d2)
{return d1._year=d2._year&&d1._month==d2._month&&d1._day==d2._day;
}void Test()
{Date d1(2002,09,21);Date d2;cout<<(d1==d2)<<endl;
}
在类里面实现:
class Date
{
public:Date(int year=2023,int month=9,int day=15){_year=year;_month=month;_day=day;} bool opprator==(const Date& d2){return _year=d2._year&&_month==d2._month&&_day==d2._day;}private:int _year;int _month;int _day;
};void Test()
{Date d1(2002,09,21);Date d2;cout<<(d1==d2)<<endl;
}
2.赋值运算符重载:
Ⅰ.赋值运算符重载格式:
参数类型:const T&,传递引用可以提高传参效率;
返回值类型:T&,返回引用可以提高返回效率,有返回值目的是为了支持连续赋值;
检测是否给自己赋值;
返回*this。
class Date
{
public:Date(int year=2023,int month=9,int day=15){_year=year;_month=month;_day=day;} bool oprator==(const Date& d2){return _year=d2._year&&_month==d2._month&&_day==d2._day;}Date& oprator=(const Date& d){if(this!=&d){_year=d._year;_month=d.month;_day=d._day;}return *this;}private:int _year;int _month;int _day;
};
Ⅱ赋值运算符只能重载成类的成员函数不能重载成全局函数:
class Date
{
public:Date(int year=2023,int month=9,int day=15){_year=year;_month=month;_day=day;} bool oprator==(const Date& d2){return _year=d2._year&&_month==d2._month&&_day==d2._day;}private:int _year;int _month;int _day;
};
Date& oprator=(const Date& d1,const Date& d2){if(&d2=&d1){d1._year=d2._year;d1._month=d2.month;d1._day=d2._day;}return d1;}
void Test()
{Date d1(2002,09,21);Date d2;cout<<(d1==d2)<<endl;
}
上面这段代码会编译失败:error C2801:“operator=”必须是非静态成员。
原因:赋值运算符如果不显示实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,所以赋值运算符只能是类成员函数。
编译器生成的默认赋值运算符重载是以值的方式逐字节拷贝。
Ⅲ.默认赋值运算符重载
上面我们讲到认编译器生成的默认赋值运算符重载是以值的方式逐字节拷贝。对于日期类来说,逐字节拷贝可以完成相应功能,但是有些情况逐字节拷贝无法完成,所以就需要深拷贝,现在我们来看看深拷贝的情况。
class Stack
{
public:Stack(size_t capacity = 10){_array = (int*)malloc(capacity * sizeof(int));if (nullptr == _array){perror("llmalloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const int& data){//检查需要扩容,但这只是演示一下深拷贝情况,所以就不写了_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:int* _array;size_t _size;size_t _capacity;
};int main()
{Stack s;s.Push(1);s.Push(2);s.Push(3);Stack s1;s1 = s;return 0;
}
上述代码会造成两个问题:
①指向同一块空间,但s改变的时候s1也会跟着改变。
②当程序结束的时候会调用两次析构函数,同一块空间析构两次,这也是导致程序奔溃的主要原因。
3.前置++和后置++重载:
同样我们用日期类来看看前置++和后置++:
class Date
{
public:Date(int year=2023,int month=9,int day=15){_year=year;_month=month;_day=day;} Date(const Date& d){_year=d._year;_month=d._month;_day=d._day;}//前置++Date& operator++(){_day++;return *this;}//前置++//C++规定:后置++重载时多增加一个整形参数,但调用函数时该参数不用传递,编译器自动传递Date operator++(int){因为前置++返回的是没有改变前的值,所以要备份一个tmp且因为出了作用域后tmp会摧毁,所以返回值不能使用引用Date tmp(*this);_day++;return tmp;}
private:int _year;int _month;int _day;
};
4.const成员:
将const修饰的成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针。
5.取地址及const取地址操作符重载:
这两个默认成员函数一般不用重新定义,编译器默认会生成,只有特殊情况才需要重载,比如想让别人读取到指定内容。
总结:
文章到这,我们关于类的六个默认成员函数已经介绍完了,也是构造函数,析构函数,拷贝构造函数,赋值运算符重载,const成员函数,取地址及const取地址操作符重载。