一、运算符重载:
1、定义:
重载操作符是具有特殊函数名的函数,关键字operator后面接需要定义的 操作符符号。操作符重载也是一个函数,具有返回值和形参表。它的形参 数目与操作符的操作数目相同,使用运算符重载可以提高代码的可读性 返回值 operator 操作符(参数列表)
(1)可以重载的运算符(除了不可重载的运算符,其余的操作符都是可以重载的操作符)
(2)不可以重载的运算符
2、赋值运算符:
(1)编译器把运算符合成了,编译器可以识别两个对象,d2=d1把d1中的内容拷贝到d2,类似于拷贝构造函数,把一个对象的内容原封不动的拷贝到另一个对象。有的情况会产生问题:若是两个对象公用同一块内存空间,一个空间销毁,会使另外一个空间成为野指针。还存在内存泄漏
(2)显式的重载赋值运算符
1‘、使用类的成员函数来写,有一个隐藏的this指针,看起来只有一个参数,实际上有两个参数。不论是返回值还是参数的位置,若是给定一个类类型的对象,最好给成引用,引用的方式不用创造临时对象。赋值操作符右操作数不发生改变,所以参数最好用const修饰
class Date
{
public:
Date(int year=290,int month=11,int day=1)
:_year(year)
, _month(month)
, _day(day)
{
cout << "Date(int):" << this << endl;
}
Date(const Date &d)
:_year(d._year)
,_month(d._month)
, _day(d._day)
{
cout << "Date(const Date &):" << this << endl;
}
void operator=(const Date&d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(122, 3, 4);
Date d2;
//Date d3(1223,4,5);
//d1=d2=d3;
d2 = d1; //d2.operator=(d1);//Date::operator=(&d2,&d1)d1:通过参数压栈传入,d2通过ecx寄存器传入
return 0;
}
缺陷不能连续赋值:如:d1=d2=d3:d3赋值给d2,d2赋值给d1:d1.operator=(d2.operator=(d3))
方式2:返回左操作数
Date& operator=(const Date&d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;//d外部实参的引用,this是当前对象,
//this和d的生命周期都比函数的生命周期长,但是我们返回this指针,因为
//需要返回左操作数
}
缺陷:不能自己给自己赋值,如:d1=d1和Date d4=&d1;……d1=d4;
方式三:判断自己给自己赋值,即this与当前的地址是否相同
Date& operator=(const Date&d)
{
if (this!=&d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
3、注意
(1)不能通过连接其他符号来创建新的操作符:比如operator@
(2)重载操作符必须有一个类类型或者枚举类型的操作数
构造一个加法的函数位于全局范围(普通的成员函数)内,这个加法函数有几个参数,给出几个参数,没有隐式的this指针)
1‘直接返回左操作数加右操作数不成立,需要给定一个自定义类型
class Date
{
public:
Date(int year=290,int month=11,int day=1)
:_year(year)
, _month(month)
, _day(day)
{
cout << "Date(int):" << this << endl;
}
Date(const Date &d)
:_year(d._year)
,_month(d._month)
, _day(d._day)
{
cout << "Date(const Date &):" << this << endl;
}
private:
int _year;
int _month;
int _day;
};
int operator + (int left, int right)
{
return left + right;
}
因为+可以直接处理整形的加号,应该给一个自定义的类型:枚举就是自定义类型
enum DATA{ ONE, TWO, THREE };
int operator + (int left, DATA right)
{
return left + right;
}
(3)用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
Date operator+(int days) {
Date tmp(*this);
tmp._day -= days;
return tmp;
}
(4)作为类成员的重载函数,其形参看起来比操作数数目少1成员函数的 操作符有一个默认的形参this,限定为第一个形参
:写成类的成员函数,若是有两个操作数,只需要给一个操作数,若是有一个操作数,就不用给了。有一个隐含的this指针。
(5)一般将算术操作符定义为非成员函数(给成普通函数,会传两个参数,更加清晰),将赋值运算符定义成员函数
(6)操作符定义为非类的成员函数时,一般将其定义为类的友元
(7)== 和 != 操作符一般要成对重载
(8)下标操作符[]:一个非const成员并返回引用,一个是const成员并 返回引用
(9)解引用操作符*和->操作符,不显示任何参数
(10)前置式++/–必须返回被增量或者减量的引用,后缀式操作符必须返 回旧值,并且应该是值返回而不是引用返回
++只有一个参数,所以给成成员函数的时候,不需要给定参数。有返回值。返回值的生命周期比函数长,则使用引用。
class Date
{
public:
Date(int year=290,int month=11,int day=1)
:_year(year)
, _month(month)
, _day(day)
{
cout << "Date(int):" << this << endl;
}
Date(const Date &d)
:_year(d._year)
,_month(d._month)
, _day(d._day)
{
cout << "Date(const Date &):" << this << endl;
}
//前置++与后置++的类型相同
//同一个作用域里面定义两个作用域和原型都相同的函数,给另外一个函数给一个参数,构成重载
//a=++b;
Date& operator++()//前置++:用加完后的新值进行返回
{
_day += 1;
return *this;
}
//a=b++;
Date operator++(int)//后置++:用原来的旧值进行返回,后面比前置++多一个参数//不需要用引用,不能返回栈里面的值,
//直接返回一个值
{
Date tmp(*this);//用一个临时变量来保存旧值
_day += 1;//当前对象加1
return *this;//返回加1之前的旧值
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(122, 3, 4);
Date d2;
d2 = d1++;
return 0;
}
后置++成员函数里面只给了一个参数,有两个参数,而实际传参的时候只传了一个参数(编译器已经替我们传递了一个参数),参数只给出了类型,没有给定值。因为后置++给一个参数是为了和前置++形成重载,所以不需要值;这里的这个参数我们不使用,但是必须给出来。
(11)输入操作符>>和输出操作符<<必须定义为类的友元函数
输出操作符不能直接输出一个对象,因为对象里面包含了多个成员,不确定是输出哪一个成员。重载输出运算符,输出运算符可以连着输出,输出运算符有两个操作数。
a、不能处理"cout<<d3;"这种输出,可以处理"d3.operator<<(cout);"和"d3<<cout;"这两种情况因为该函数(如下)有两个参数,第一个参数是当前调用对象的地址(this),所以cout不能出现在左边,cout是右操作数。
class Date
{
public:
Date(int year = 2943, int month = 243,int day = 2)
:_year(year)
, _month(month)
, _day(day)
{
}
void operator<<(ostream & _cout)//ostream输出流对象
{
_cout << _year << "/" << _month << "/" << _day;//输出运算符的重载不用换行,外部用户自己定义
}
private:
int _year;
int _month;
int _day;
};
void TestDate()
{
Date d1(234, 5, 6);
Date d2(d1);
Date d3;
d3 = d2;
d3.operator<<(cout);
d3 << cout;
}
b、输出操作符的本意是“cout<<d3;”把d3写入到,再把对象输出到控制台;上面的方式"d3<<cout;"把cout当成了参数(放到了对象之中。不符合常规。改进:不把输出操作符重载成类的成员函数(全局函数),给成普通的函数(括号之中有几个参数,就有几个参数),但是此程序会产生错误,因为成员变量是私有的。
class Date
{
public:
Date(int year = 2943, int month = 243,int day = 2)
:_year(year)
, _month(month)
, _day(day)
{
}
private:
int _year;
int _month;
int _day;
};
void operator<<(ostream &_cout, const Date&d)//此时cout作为左操作数,要打印中的内容作为右操作数
{
_cout << d._year << "/" << d._month << "/" << d._day;
}
void TestDate()
{
Date d1(234, 5, 6);
Date d2(d1);
Date d3;
d3 = d2;
d3.operator<<(cout);
d3 << cout;
}
程序产生错误:类外函数访问类中私有成员,改进:
方法1:写一些公有的成员函数,将类中的私有成员获取出来,调用这些公有的接口实现
方法2:友元
class Date
{
friend ostream& operator<<(ostream& _cout, const Date &d);
public:
Date(int year = 2943, int month = 243,int day = 2)
:_year(year)
, _month(month)
, _day(day)
{
}
private:
int _year;
int _month;
int _day;
};
ostream &operator<<(ostream &_cout, const Date&d)//此时cout作为左操作数,对象中的内容作为右操作数
{
_cout << d._year << "/" << d._month << "/" << d._day;
return _cout;//cout的生命周期比函数长,需要返回引用
}
void TestDate()
{
Date d1(234, 5, 6);
Date d2(d1);
Date d3;
cout << d3 << endl;
}
c、不能连着输出,因为应该有返回值
ostream &operator<<(ostream &_cout, const Date&d)//此时cout作为左操作数,对象中的内容作为右操作数
{
_cout << d._year << "/" << d._month << "/" << d._day;
return _cout;//cout的生命周期比函数长,需要返回引用
}
实现日期函数:加、减,两个日期相减,两个日期比大小(>,<,==,!=),前置++,--,后置++,--(日期需要考虑合法性以及是否为闰年)//哪些类需要给出拷贝构造函数,赋值运算符,析构函数?类中涉及到资源的管理,申请空间……的需要给出这三个函数。
class Date
{
public:
Date(int year = 2943, int month = 243,int day = 2)
:_year(year)
, _month(month)
, _day(day)
{
}
//比大小
bool operator>(const Date& d)
{
if (_year > d._year ||
_year == d._year&&_month > d._month ||
_year == d._year&&_month == d._month&&_day > d._day)
{
return true;
}
return false;
}
bool operator==(const Date &d)
{
return _year == d._year&&
_month == d._month&&
_day == d._day;
}
bool operator!=(const Date& d)
{
return !(*this == d);//相等返回真,取反即为假
}
//a=++b;
Date& operator++()//前置++:用加完后的新值进行返回
{
_day += 1;
return *this;//this的生命周期比函数长
}
//a=b++;
Date operator++(int)
{
Date tmp(*this);
_day += 1;
return *this;
}
private:
int _year;
int _month;
int _day;
};
知道一个函数的运行时间(精确计算)(包括cpu的精度)
函数调用过程的理解,如下函数:
(1)返回临时变量
class Test
{
public:
Test()
{
cout << "Test():" << this << endl;
}
Test(const Test& t)
{
cout << "Test(const Test& t):" << this << endl;
}
//赋值运算符的重载
Test& operator=(const Test& t)
{
cout << this << "=" << &t << endl;
if (this != &t)
{
;//赋值操作
}
return *this;
}
~Test()
{
cout << "~Test():" << this << endl;
}
};
Test FunTestc(Test t)
{
Test tt1;
Test tmp(t);
tt1 = tmp;
return tt1;
}
void TestFunc()
{
Test t1;
Test t2;
t2 = FunTestc(t1);
}
int main()
{
TestFunc();
return 0;
}
(2)返回引用
Test &FunTestc(Test &t)
{
Test tt1;
Test tmp(t);
tt1 = tmp;
return t;
}
void TestFunc()
{
Test t1;
Test t2;
t2 = FunTestc(t1);
}