运算符重载基本概念
1)目的是拓展原C程序运算符的作用范围,使程序看起来更加简洁
2)本质是函数,可以称之为运算符函数
3)可以定义为普通函数,也可定义为成员函数
4)把含运算符的表达式转换成函数的调用
5)运算符操作数转换为函数的参数
6)运算符函数可以重载,调用时根据参数类型选择
例如:
class complex{
public:
double real, imag;
complex(double r = 0.0, double i = 0.0) :real(r), imag(i){}
complex operator - (const complex & r);
};
complex operator+(const complex &c1, const complex &c2){
return complex(c1.real+c2.real,c1.imag+c2.imag);
}
complex complex::operator-(const complex &r){
return complex(real-r.real,imag-r.imag);
}
int main(){
complex a(4, 4), b(1,1), c;
c = a + b;//等价于c=operator+(a,b);
cout << c.real << ',' << c.imag << endl;
cout << (a - b).real << ',' << (a - b).imag << endl;//等价于a.operator-(b);
return 0;
}
赋值运算符重载
1)考虑需要将两个不同类型的变量相互赋值,比如将 char* 类型的字符串常量赋值给字符串对象 complex
2)只能是成员函数
如示例:实现 s=“hello" 功能
class String{
private:
char * str;
public:
String() :str(new char[1]){
str[0] = 0;
}
~String(){
delete[] str;//析构函数 负责当对象消亡时将str指向的空间释放
}
const char * c_str(){
return str;
}
String& operator=(char * s);//声明赋值运算符成员函数
};
String& String::operator=(char* s){//主义函数返回值为对象的引用
delete[]str;//先将成员指针str指向的空间delete掉
str = new char[strlen(s) + 1];//再new出一片新的字符数组内存空间,空间大小为 s指向的空间大小加一
strcpy(str, s);//然后将S指向的空间复制到str指向的空间 完成赋值
return *this;//最后返回被赋值的对象
}
int main(){
String s1, s2;
s1 = "hello";
s2 = "nice to meet you";
cout << s1.c_str() << endl << s2.c_str() << endl;
return 0;
}
输出:
hello
nice to meet you
3)如果需要实现 s1=s2 的赋值语句,则上诉代码存在问题
4)我们希望将s2.str指向的空间赋值给s1.str指向的空间,而单纯 s1=s2的结果是使得s1.str与s2.str相同,即s1.str与s2.str同指向同一片内存空间
5)这样就会导致当s1消亡时,及时s2没有消亡,但s2.str所指向的空间已经被析构函数释放了,这样不妥
6)改进方式:重新定义类string的赋值运算符函数
例如:
String& operator=(const String & r);
String& String::operator=(const String & r){
delete[] str;
str = new char[strlen(r.str) + 1];
strcpy(str, r.str);
return *this;
}
7)如果出现 s1=s1 的语句 则上诉代码会出现问题
8)这个语句会使得 s1.str 所指向的空间先释放掉 从而导致无法进行后面的赋值
所以改进:
String& String::operator=(const String & r){
if (this==&r)//判断赋值语句两端的对象是否相同
return *this;
delete[] str;
str = new char[strlen(r.str) + 1];
strcpy(str, r.str);
return *this;
}
9)如果出现调用复制构造函数的情况,就会出现如上诉 s1=s2 一样的问题
10)需要重新定义新的复制构造函数
例如:
String(const String& e){
str = new bar[strlen(e.str)+1];
strcpy(str,e.str);
}
11)为了实现类似 (s1=s2)=s3(使得s1=s3) 的功能,赋值运算符函数的返回值需要是对象的引用(只有返回值是引用的函数可以放在等号的左侧)
运算符重载为友元函数
1)有时需要将运算符函数定义为普通函数
例如:
class complex{
double real, imag;
public:
complex(double r, double i) :real(r), imag(i){}
complex operator+(int r){
return complex(real + r, imag);
}
};
2)如上的运算符函数operator+可以解释类似于 c=c+5; 的代码,但不能解释类似于 c=5+c 的代码
3)所以需要将运算符函数定义为普通函数来实现上诉功能
例如:
complex operator+(double c,const complex & s){
return complex(s.real+c,s.imag);
}
4)但为了使得普通函数可以访问对象的私有成员变量,需要将其声明为友元
例如:
class complex{
double real, imag;
public:
complex(double r, double i) :real(r), imag(i){}
void c_print(){
cout << real << ',' << imag << endl;
}
complex operator+(double r){
return complex(real+r,imag);
}
friend complex operator+(double c, const complex & s);
};
赋值运算符重载应用 可变长数组实现
1)在应用中,如何能使得一个数组的长度可以改变,例如可以实现如下整型数组类的功能
int main(){
CArray a;//定义一个类CArray对象,其代表一个空数组(需要定义构造函数)
for (int i = 0; i < 5;++i)
a.push_back(i);//可以通过函数为数组向后增加一个整型元素 i(需要定义push_back函数)
CArray a2, a3;
a2 = a;//可以通过赋运算符 = 将 a 数组内容赋值给 a2 数组内容(需要赋值运算符重载函数)
for (int i = 0; i < a2.length(); ++i)//可以用函数返回数组长度(需要定义length函数)
cout << a2[i] << " ";//可以通过[]运算符返回数组对应元素的值(需要定义[]运算符重载函数)
a2 = a3;
for (int i = 0; i < a2.length(); ++i)
cout << a2[i] << " ";
cout << endl;
a[3] = 100;//可以通过[]运算符进行对数组对应元素赋值的工作
CArray a4(a);//可以通过构造函数完成两数组内容的复制(需要定义复制构造函数)
for (int i = 0; i < a4.length(); ++i)
cout << a4[i] << " ";
return 0;
}
2)类 CArray 定义如下
class CArray{
private:
int size;//用于储存数组元素的个数
int * p;//用于指向整型数组空间
public:
CArray(int s = 0){//构造函数可以根据所给参数完成数组空间的分配
size = s;//储存数组元素的个数
if (s == 0)//当参数为零 则代表数组为空
p = NULL;//指针置空
else//否则 数组元素为 s
p = new int[s];//动态分配一片大小为sizeof(int)*s的空间 将其地址赋值给指针
}
CArray(CArray& p1){//复制构造函数
if (!p1.p){//如果要复制的数组为空
p = NULL;//则指针置空
size = 0;//元素数为零
return;//返回
}
p = new int[p1.size];//否则 重新分配空间
memcpy(p, p1.p, sizeof(int)*p1.size);//并完成内存字节的复制
size = p1.size;//储存元素数
}
~CArray(){//析构函数
if (p)//如果指针不为空
delete[] p;//则将所指空间释放
}
void push_back(int i);//push_back函数声明
int length();//length()函数声明
CArray & operator=(const CArray p1);//赋值运算符重载函数声明
int & operator[](int i){//[]运算符重载函数 返回是int &(为使得a[1]=3合法)
return p[i];//返回相应数组元素 引用使得返回值和数组元素一回事
}
};
3)push_back函数
void CArray::push_back(int i){//再数组后再增加一个元素 i
if (p){//判断原数组不为空
int * tmp;//定义一个中间指针
tmp = new int[size + 1];//使中间指针指向一个大小比原数组大一个元素的数组空间
memcpy(tmp, p, sizeof(int)*size);//将原数组内容复制到中间指针指向的数组空间
delete[]p;//将原数组空间释放
p = tmp;//将中间指针赋值给作用数组 此时原数组的空间多了一个元素空间的大小
}
else//原数组为空
p = new int[1];//则直接动态分配一个元素大小的空间
p[size++] = i;//将size自加 并将最后的元素赋值为 i
}
4)length()函数
int CArray::length(){
return size;//直接返回size
}
5)赋值运算符重载函数
CArray & CArray::operator=(const CArray p1){
if (p == p1.p)//判断赋值运算符两端为同一个对象
return *this;//则直接返回
if (p1.p == NULL){//判断赋值右端数组为空数组 则将被赋值数组置空
if (p)//判断被赋值数组不是空
delete[] p;//则将所指空间释放
p = NULL;//再将指针置空
size = 0;//然后将元素数置零
return *this;//最后返回
}
if (size < p1.size){//判断原数组空间大小比赋值数组小
if (p)//原数组不为空
delete[] p;//则将所指空间释放
p = new int[p1.size];//并重新分配一个与赋值数组空间一样大小的空间
}
memcpy(p, p1.p, sizeof(int)*p1.size);//空间分配后 或空间够用 则完成两数组空间内容的复制
size = p1.size;//修改数组元素数
return *this;//最后返回
}
左移右移运算符重载
1)cout是 iostream 文件中定义的 ostream类的对象
2)cin是istream类的对象
3)运算符"<<",">>“分别在ostream类与istream类中进行了重载
4)故"cout<<5"的调用形式是"cout.operator(5)”
5)为了能够实现类似于"cout<<5<<“hello”"的语句,重载函数ostream::operator()的返回值类型应该是ostream类的引用:ostream &
例1:实现如下代码 使能够输出 5hello
class student{
public:
int age;
};
ostream & operator<<(ostream & o, const student & s){//需要定义为普通函数 函数参数等于运算符操作数(2) 为了减小开销函数参数使用对象的引用
o << s.age;
return o;
}
int main(){
student s;
s.age = 5;
cout << s << "hello";
return 0;
}
输出:
5hello
例2:假定c是complex复数类的对象,现在希望写"cout<<c",就能以"a+bi"的形式输出c的值,写"cin>>c",就能从键盘中读入"a+bi"并赋值给c,使得c.real=a,c.imag=b。
class complex{
private:
double real, imag;
public:
complex(double r = 0, double i = 0) :real(r), imag(i){}
friend ostream& operator<<(ostream& os, const complex & c);//将重载函数声明为友元
friend istream& operator>>(istream & is, complex& c);//声明友元
};
ostream& operator<<(ostream& os, const complex & c){
os << c.real << '+' << c.imag << 'i' ;
return os;
}
istream& operator>>(istream & is, complex& c){
string s;
is >> s;
int pos = s.find('+',0);
string stmp = s.substr(0, pos);
c.real = atof(stmp.c_str());
stmp = s.substr(pos + 1, s.length() - pos - 2);
c.imag = atof(stmp.c_str());
return is;
}
int main(){
complex c;
int n;
cin >> c >> n;
cout << c << ' ' << n;
return 0;
}
输入:
123+13.1i 456
输出:
123+13.1i 456
重载类型转换运算符
1)重载的类型转换运算符有显性和隐性
2)声明定义时不用写返回值类型 因为返回值类型和运算符相同
3)单目运算符 操作数为一 声明成员函数时不用写参数
class complex{
private:
double real, imag;
public:
complex(double r = 0, double i = 0) :real(r), imag(i){}
operator double(){
return real;
}
};
int main(){
complex c(1.2, 3.2);
cout << (double)c << endl;//显性作用 输出1.2
double n = 2 + c;//隐性作用 等价于2+c.operator double ();
cout << n << endl;//输出3.2
return 0;
}
输出:
1.2
3.2
自增自减运算符重载
1)自增自减运算符有前置和后置之分
2)C++规定 前置时为一元运算符 后置时为二元运算符
3)前置运算符重载函数返回引用
例如:
class CDemo{
private:
int n;
public:
CDemo (int i = 0) :n(i){}
CDemo & operator++();//前置++
CDemo operator++(int );//后置++
operator int(){//类型转换
return n;
}
friend CDemo & operator--(CDemo &);//前置--
friend CDemo operator--(CDemo &, int);//后置--
};
CDemo & CDemo::operator++(){//前置++
n++;
return *this;//返回原对象
}//++s等价于s.operator++()
CDemo CDemo::operator++(int i){//后置++
CDemo tmp(*this);//保存修改前的值
n++;
return tmp;//返回修改前的值
}//s++等价于s.operator(0)
CDemo & operator--(CDemo & r){//前置-- 参数得是引用
r.n--;
return r;//返回修改后的对象
}//--s等价于s.operator(s)
CDemo operator--(CDemo & r, int k){//后置--
CDemo tmp(r);//保存修改前的值
r.n--;//修改
return tmp;//返回修改前的值
}//s++等价于s.operator(s,0)
int main(){
CDemo d(5);
cout << d++ << ',';
cout << d << ',';
cout << ++d << ',';
cout << d << endl;
cout << d-- << ',';
cout << d << ',';
cout << --d << ',';
cout << d << endl;
return 0;
}
输出:
5,6,7,7
7,6,5,5
来源:CSDN
作者:weixin_45644911
链接:https://blog.csdn.net/weixin_45644911/article/details/104161705