运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似
函数名字为:关键字operator后面接需要重载的运算符符号 原型:返回值类型 operator操作符(参数列表)
注意:
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型参数
- 运算符重载只对自定义类型,内置类型的运算符含义不变;
- 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
- .* :: sizeof ?: . 注意以上5个运算符不能重载
class Date { public: Date(int year = 1900, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } // bool operator==(Date* this, const Date& d2) // 这里需要注意的是,左操作数是this,指向调用函数的对象 bool operator==(const Date& d2) { return _year == d2._year; && _month == d2._month && _day == d2._day; } private: int _year; int _month; int _day; };
注:对象赋值操作是作用于两个已经实例化好的对象
赋值运算符只能重载成类的成员函数不能重载成全局函数
赋值运算符重载格式
- 参数类型:const T&,传递引用可以提高传参效率
- 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
- 检测是否自己给自己赋值
- 返回*this :要复合连续赋值的含义
问题1 如何支持连续赋值?
因为 = 赋值运算符计算顺序从右往左,我们直接return *this; 出了作用域this还会存在,同时返回引用,减少拷贝函数调用。同时防止 自己给自己赋值问题 if(this != d) 来控制是否赋值。
用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值
既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?
// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。 typedef int DataType; class Stack { public: Stack(size_t capacity = 10) { _array = (DataType*)malloc(capacity * sizeof(DataType)); if (nullptr == _array) { perror("malloc申请空间失败"); return; } _size = 0; _capacity = capacity; } void Push(const DataType& data) { // CheckCapacity(); _array[_size] = data; _size++; } ~Stack() { if (_array) { free(_array); _array = nullptr; _capacity = 0; _size = 0; } } private: DataType *_array; size_t _size; size_t _capacity; }; int main() { Stack s1; s1.Push(1); s1.Push(2); s1.Push(3); s1.Push(4); Stack s2; s2 = s1; return 0; }
注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现
前置++和后置++重载
前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1
//++d1 Date& Date::operator++() { *this += 1; return *this; } //d1++ Date& Date::operator++(int) { Date tmp(*this); *this += 1; return tmp; }
取地址及const取地址操作符重载
这两个默认成员函数一般不用重新定义 ,编译器默认会生成
const成员
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
const 权限问题: 如果对const变量进行写入,编译器会报错,因为const变量属性只可读,可读可写属于权限放大。利用const只可读我们可以应用到成员函数上:
//const 修饰 *this //this的类型变成 cosnt A* class A { void Print() const //此处为语法规定,函数旁加const修饰 *this 记住就好 { cout << _a << endl; } private: int _a =10; };
注:
内部不改变成员变量的成员函数,最好加上const,这样const对象和普通对象都可以调用,但不会修改成员变量值
const 只适用于成员函数,因为此语法修饰*this指针,只有成员函数有默认this指针
成员函数如果声明和定义分离 都要加const