类和对象详解(下)-----运算符重载
目录
1.运算符重载
2.赋值运算符重载
3.取地址运算符重载
3.1const成员函数
3.2取地址运算符重载
1.运算符重载
什么是运算符重载呢?简单举个例子就懂了。
就是我想实现日期的加法,而“+”这个运算符C++只实现了内置类型的加法,而我们要想实现日期类的加法就得自己写,如下:
#include<iostream>
using namespace std;
class Date {
public:
int _year;
int _month;
int _day;
Date(int year=1, 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+(int a)
{
Date d1(this->_year, this->_month, this->_day + a);
return d1;
}
void print()
{
cout << this->_year << " " << this->_month << " " << this->_day;
}
};
int main()
{
Date d1(2025,2,16);
d1 = d1 + 5;
d1.print();
return 0;
}
现在我们就可以运行了(注意,日期的加法并没有那么简单,这里只是简单写了一下),我们细细拆解一下,为什么刚才加号不能用,现在就能用了呢?
来看重载函数
Date operator+(int a);
前面的Date是这个函数的返回值,operator+代表是对加号进行运算符重载,然后括号里面代表需要的参数,现在我们来回忆加法的使用情况首先,加法需要两个操作数,一定是A+B,两个操作数,而函数的参数就是从左到右对应的操作数,那有的同学就会注意到,我这里面的函数参数只有一个,这里就要注意,我是类内实现的,类内实现默认这个类的对象为第一个操作数,接下来我写到类外给大家看一下:
#include<iostream>
using namespace std;
class Date {
public:
int _year;
int _month;
int _day;
Date(int year=1, 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;
}
void print()
{
cout << this->_year << " " << this->_month << " " << this->_day;
}
};
Date operator+(Date d,int a)
{
Date d1(d._year, d._month, d._day + a);
return d1;
}
int main()
{
Date d1(2025,2,16);
d1 = d1 + 5;
d1.print();
return 0;
}
同样这个代码也可以正常运行,那我们接下来来验证一下从左到右的,我们稍微修改一下代码,
仅把两个操作数的位置换了,就出现了报错,印证了我们之前的说法,接着给大家展示一下常用的一种写法,类内声明,类外实现,也就是域名的使用
#include<iostream>
using namespace std;
class Date {
public:
int _year;
int _month;
int _day;
Date(int year=1, 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+(int a);
void print()
};
Date Date::operator+(int a)
{
Date d1(_year, _month, _day + a);
return d1;
}
void Date::print()
{
cout << this->_year << " " << this->_month << " " << this->_day;
}
int main()
{
Date d1(2025,2,16);
d1 = d1 + 5;
d1.print();
return 0;
}
为什么要类内声明,类外使用呢? 目的是增强代码的可读性,而且默认不是内联(内联函数太长的话影响代码效率)。
注意不是所有运算符都可以重载的,有五个就不能重载:
.* :: sizeof ?: .
且
重载操作符⾄少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义,如: int operator+(int x, int y)
这里再给大家介绍一种运算符的重载,即对“<<”和“>>”的重载
#include<iostream>
using namespace std;
class Date {
public:
int _year;
int _month;
int _day;
Date(int year=1, 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;
}
void operator<<(ostream&out);
};
void Date::operator<<(ostream& out)
{
out << _year << " " << _month << " " << _day;
}
int main()
{
Date d1(2025,2,16);
cout << d1;
return 0;
}
这里我们可以看到,实现的代码,但是他会报错,why?
因为我们将其当成了类内的成员函数,此时成员为第一个变量,而<<这个运算符有两个操作数cout<<A本应cout为第一个变量,但此时成员为一个变量,那我们怎么让他发挥出作用呢。就是这样
而这样很明显不符合我们的使用习惯,所以怎么解决呢?答案就是不要将其放到类内,直接类外实现
#include<iostream>
using namespace std;
class Date {
public:
int _year;
int _month;
int _day;
Date(int year=1, 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;
}
};
void operator<<(ostream& out,const Date&d)
{
out << d._year << " " << d._month << " " << d._day;
}
int main()
{
Date d1(2025,2,16);
cout<<d1;
return 0;
}
这样就可以了。这里还有一个比较容易犯的错误,顺便给大家说一下
#include<iostream>
using namespace std;
class Date {
public:
int _year;
int _month;
int _day;
Date(int year = 1, 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+(int a);
void print();
};
Date&Date::operator+(int a)
{
Date d1(_year, _month, _day + a);
return d1;
}
void Date::print()
{
cout << this->_year << " " << this->_month << " " << this->_day;
}
void test()
{
int d = 3;
int a = 3;
int e = 3;
int w = 3;
int c = 3;
}
int main()
{
Date d1(2025, 2, 16);
Date&d2 = d1 + 5;
test();
d2.print();
return 0;
}
运行结果
为什么呢?我们在进行运算符重载的时候,返回值为引用返回
他返回了d1,不会进行拷贝构造,而d1是个局部变量,当函数结束的时候就会销毁,而我们在外部用一个引用来接收,此时d2就是局部变量的别名了, 然后再调用一个函数,在里面创建一些变量,就可能把旧的局部变量的空间上的值破坏掉,就会出现这些莫名其妙的错误了。
2.赋值运算符重载
特点:
1. 赋值运算符重载是⼀个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成 const 当前类类型引⽤,否则会传值传参会有拷⻉
2. 有返回值,且建议写成当前类类型引⽤,引⽤返回可以提⾼效率,有返回值⽬的是为了⽀持连续赋 值场景。
3. 没有显式实现时,编译器会⾃动⽣成⼀个默认赋值运算符重载,默认赋值运算符重载⾏为跟默认拷 ⻉构造函数类似,对内置类型成员变量会完成值拷⻉/浅拷⻉(⼀个字节⼀个字节的拷⻉),对⾃定义 类型成员变量会调⽤他的赋值重载函数。
他和之前的拷贝构造差不多,一般在动态开辟之后才需要我们自己显式的去实现赋值运算符的重载,其他时候一般默认的就够了
3.取地址运算符重载
3.1const成员函数
• 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后⾯。
• const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改。 const 修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this 变为 const Date* const this
具体的应用场景,如果d1用const修饰,那我们就不能用一些函数,因为可能会修改它的值的风险(属于权限放大),所以我们这时就能加入const成员函数
#include<iostream>
using namespace std;
class Date {
public:
int _year;
int _month;
int _day;
Date(int year = 1, 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+(int a);
void print()const;
};
Date&Date::operator+(int a)
{
Date d1(_year, _month, _day + a);
return d1;
}
void Date::print()const
{
cout << this->_year << " " << this->_month << " " << this->_day;
}
int main()
{
const Date d1(2025, 2, 16);
d1.print();
return 0;
}
所以我们一般能加就加,注意这里可以权限缩小,如
这里编译器是允许的。
3.2取地址运算符重载
取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器⾃动 ⽣成的就可以够我们⽤了,不需要去显⽰实现。除⾮⼀些很特殊的场景,⽐如我们不想让别⼈取到当前类对象的地址,就可以⾃⼰实现⼀份,胡乱返回⼀个地址。