目录
1.运算符重载:1.可实现的对象
编辑2.定义运算符重载函数的一般格式:
3.运算符的重载实际
2.为什么要重载?
3.前提:
4.如何重载?
5.默认
6.指针作为数据成员
7.字符串重载
8.友元重载:重载输出<<运算符
9.运算符重载函数的总结
10.重载运算符有以下几种限制
1.运算符重载:
1.可实现的对象
--让类类型的对象像基本数据类型一样去操作,例如,可以实现对象+,-,*,/,%,==,! = [] () << >>等
2.定义运算符重载函数的一般格式:
返回类型 operator 运算符(参数列表) 比如:operator=(const A&str)
返回值类型 类名::operator重载的运算符(参数表){……}
operator是关键字,它与重载的运算符一起构成函数名。因函数名的特殊性,C++编译器可以将这
类函数识别出来。
3.运算符的重载实际
是一种特殊的函数重载,必须定义一个函数,并告诉C++编译器,当遇到该重载的运算符时调用此函数。这个函数叫做运算符重载函数,通常为类的成员函数。
2.为什么要重载?
如何实现两个同类的对象相加?
class A
{
public:
A(int i=0):m_i(i){}
private:
int m_i;
};
void main()
{
A a(5), b(7);
cout<<a+b<<endl;
}
我们发现两个A类对象并不能直接相加。
class A
{
public:
A(int i=0):m_i(i){}
private:
int m_i;
};
class STR
{
public:
STR(const char* str = "\0")
{
m_str = new char[strlen(str) + 1];
strcpy_s(m_str, strlen(str) + 1, str);
}
private:
char* m_str;
};
void main()
{
A a(5), b(7);
//cout<<a+b<<endl;//两个A类的对象,不能直接相加
int aa = 5, bb = 7;
cout << aa + bb << endl;
STR s("1234567");
string str = "123456";
string s1 = "123", s2 = "456";
string s3 = s1 + s2;
cout << str << endl;
str[3] = 'k';
cout << str << endl;
cout << "s3= " << s3 << endl;
//s[2] = 'k';
}
我们发现如果将我们写的类的内容,直接写为int这一大类的内容相加的时候是可以实现的,于是我们可以通过之前的接口,将A类的i直接获得,变为两个int类的相加
class A
{
public:
A(int i = 0,int j=0) :m_i(i),m_j(j)
{
}
const int& GetI()const
{
return m_i;
}
const int& GetJ()const
{
return m_j;
}
A ADD(const A& b)
{
return A(m_i+b.m_i,m_j+b.m_j);
}
void Print()
{
cout << m_i << " " << m_j << endl;
}
private:
int m_i;
int m_j;
};
void main()
{
A a(5), b(7);
//cout<<a+b<<endl;
//cout << a.m_i + b.m_i << endl;//error//本质上就是把对象的数据成员进行相加
cout << a.GetI() + b.GetI() << endl;
cout << a.GetJ() + b.GetJ() << endl;
a.ADD(b).Print();
}
看起来不直观,可读性低
并且当类的成员越来越多时,这样显得非常麻烦
3.前提:
void main()
{
int a = 10, b = 3;
int c = 0;
c = a + b;
//a+b=c;//error
++a = c;
//b++=13;//error
cout << a << endl;
(a = b) = c;
}
1.c = a + b;//从a的内存空间把a的值取出来,从b的内存空间把b的值取出来,把相加过的值放在c的空间
2.a+b=c;//error//从c的内存空间把c的值取出来,a+b(的空间是临时空间,表达式结束,空间就释放了)没有确定的(永久)空间存放c
3.++a = c;//(现在的a=11=执行过表达式后的a的值)将c放到a的存储单元
4.//b++=13;//error//(b=3,b++=4,值不一样,b++没有确定空间)
4.如何重载?
class A
{
public:
A(int i=0):m_i(i)//构造函数可作为类型转换
{
}
void Print()
{
cout << m_i << endl;
}
A operator+(const A&b)//本质是实现对象的数据成员的加法
{
return m_i + b.m_i;
}
//A operator-(const A& a)//b-a b.operator-(a)本质是实现对象的数据成员的减法
//{
// return this->m_i - a.m_i;
//}
A operator-(const A& s)//b-a b.operator-(a)本质是实现对象的数据成员的减法
{
return this->m_i - s.m_i;
}
A& operator++()//++a a.++
{
++m_i;//将a自己的数据成员进行修改
return *this;//修改完后,返回自己本身
}
/*
* b++,表达式的值是,b没加的值,执行完成后,b+1,所有在重载之后++,得体现两种值
*/
A operator++(int)//int 没有实际性作用,只是为了区分前置++
{
int t = m_i;//保存自己
m_i = m_i + 1;//+1
return t;//返回没加以前保存的值
//return m_i++//等价与上面三句话
}
private:
int m_i;
};
int main()
{
A a(2), b(6);
(a + b).Print();
(b - a).Print();
(a - b).Print();
(++a).Print();
(b++).Print();
b.Print();
}
(a + b).Print();//a.+(b)//成员形式//(a,b)//友元
(b - a).Print();//b.-(a);
(a - b).Print();//a.-(b)
(++a).Print();//3
(b++).Print();//b加之前//6
b.Print();//上式+1 7
5.默认
如果程序员没有提供赋值运算符,则类会提供一个默认的,默认的功能为:
用右边的对象的数据成员依次给左边对象的数据成员赋值
class A
{
public:
A(int i = 0, int j = 0) :m_i(i), m_j(j)
{
}
void Print()
{
cout << m_i << " " << m_j << endl;
}
private:
int m_i;
int m_j;
};
void main()
{
A a(2, 6);
A b;
a.Print();
b.Print();
b = a;
b.Print();
}
a.Print();// 2 6
b.Print();// 0 0
b = a;// 2 6用a的值重新给b赋值,调用了默认的赋值运算符重载函数
b.Print();
6.指针作为数据成员
class Person
{
public:
Person(const char*name="\0")
{
m_name = new char[strlen(name) + 1];
strcpy_s(m_name, strlen(name) + 1, name);
}
void Print()
{
cout << m_name << endl;
}
~Person()
{
delete[]m_name;
m_name = NULL;
}
Person& operator=(const Person& s)//析构+拷贝构造
{
if (this == &s)//判断自赋值
return *this;
delete[]m_name;
m_name = new char[strlen(s.m_name) + 1];
strcpy_s(m_name, strlen(s.m_name) + 1, s.m_name);
return *this;
}
private:
char* m_name;
};
void main()
{
Person p1("lisi");
Person p2("11");
p1.Print();
p2.Print();
p2 = p1;
p2.Print();
}
总结:
如果有指针作为数据成员,则赋值运算符重载也要写出来,让两个而不同的对象的指针分别指向内存单元,不过里面的内容保持相同
* 赋值=能作为左值,所以引用返回
if (this == &s) //判断自赋值
return *this;
//先将左边对象原本的内存空间释放掉
delete[]m_name;
//接着开辟和右边对象s相同大小的内存单元
m_name = new char[strlen(s.m_name) + 1];
strcpy_s(m_name, strlen(s.m_name) + 1, s.m_name);
p2 = p1; //用p1给p2重新赋值,如果调用默认的赋值重载,则让p2的指针重新指向了p1的指针,p1和p2的指针指向同一块空间,但是p2原本的空间还在
7.字符串重载
class STR
{
public:
STR(const char* str = "\0")
{
m_str = new char[strlen(str) + 1];
strcpy_s(m_str, strlen(str) + 1, str);
}
STR(const STR& s)
{
m_str = new char[strlen(s.m_str) + 1];
strcpy_s(m_str, strlen(s.m_str) + 1, s.m_str);
}
~STR()
{
cout << "~STR " << m_str << endl;
if (m_str != NULL)
{
delete[]m_str;
m_str = NULL;
}
}
STR operator+(const STR& s)//a+b 其实就是将两个对象中的字符串合并
{
char* temp = new char[strlen(m_str) + strlen(s.m_str) + 1];
strcpy_s(temp, strlen(m_str)+1, m_str);
strcat_s(temp,strlen(m_str)+strlen(s.m_str)+1, s.m_str);
STR ss(temp);
delete[]temp;
temp = NULL;
return ss;
}
STR& operator=(const STR& s)
{
if (this == &s)
return *this;
delete[]m_str;
m_str = new char[strlen(s.m_str) + 1];
strcpy_s(m_str, strlen(s.m_str) + 1, s.m_str);
return *this;
}
void Print()
{
cout << "m_str = "<<m_str << endl;
}
char& operator[](int index)
{
if (index >= 0 && index < strlen(m_str)) //"1234"
return m_str[index];
}
int operator==(const STR& s)
{
return strcmp(m_str, s.m_str)==0;
}
private:
char* m_str;
};
void main()
{
STR a("111"), b("222");
cout << "a:" << endl;
a.Print();
cout << "b:" << endl;
b.Print();
STR c = a + b;//拷贝构造 111222
cout << "c:" << endl;
c.Print();//111222
STR d;
d = c;//赋值=运算符重载
cout << "d:" << endl;
d.Print();//111222
c[5] = 'h';
cout << "c[5]" << endl;
cout << c[5] << endl;//11122h
cout << "c:" << endl;
c.Print();
cout << "a==b" << endl;
cout << (a == b) << endl;//0
cout << "a!=b" << endl;
cout << (a != b) << endl;//1
}
8.友元重载:重载输出<<运算符
cout--ostream类的对象
cin--istream类的对象
cout << a;
一般情况下运算符可以解析成下面两种形式
1.cout.<<(a) ostream类中重载了<<,程序员不能修改
2.<<(cout,a) 可以在程序员自己定义的类中将<<重载成友元
cout<<a<<b;
cout<<a这个表达式作为了上面的表达式<<左边 返回值为引用
(cout<<a) << b 表达式执行完后,还能继续用<<,说明返回值为ostream类型
friend ostream& operator<<(ostream &out,A &a)
class Magic
{
double x;
public:
Magic(double d=0.00):x(fabs(d)){}
Magic operator+(Magic c)
{
return Magic(sqrt(x * x + c.x * c.x));
}
friend ostream& operator << (ostream& os, Magic c);
};
ostream& operator << (ostream& os, Magic c)
{
return os << c.x;
}
void main()
{
Magic ma;
cout << ma << ',' << Magic(-8) << "," << ma + Magic(-3) + Magic(-4);
}
9.运算符重载函数的总结
1、运算符重载函数的函数名必须为关键字operator加一个合法的运算符。在调用该函数时,将右 操作数作为函数的实参。
2、当用类的成员函数实现运算符的重载时,运算符重载函数的参数(当为双目运算符时)为一个 或(当为单目运算符时)没有。运算符的左操作数一定是对象,因为重载的运算符是该对象的成员函 数,而右操作数是该函数的参数。
3、单目运算符“++”和“--”存在前置与后置问题。 前置“++”格式为: 返回类型 类名::operator++(){……} 而后置“++”格式为: 返回类型 类名::operator++(int){……} 后置“++”中的参数int仅用作区分,并无实际意义,可以给一个变量名,也可以不给变量名。
4、C++中只有极少数的运算符不允许重载。
还有 # , ## , // , / * */
10.重载运算符有以下几种限制
不可臆造新的运算符.
不能改变运算符原有的优先级、结合性和语法结构,不能改变运算符操作数的个数.
运算符重载不宜使用过多.
重载运算符含义必须清楚,不能有二义性