c++面向对象程序设计学习总结之运算符重载
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了c++面向对象程序设计学习总结之运算符重载,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含10350字,纯文字阅读大概需要15分钟。
内容图文
所谓运算符重载,就是运算符本身的功能不足以实现我们的目的,我们需要赋予运算符新的含义来满足需求。(重载的运算符必须与用户定义的自定义类型的对象一起使用,至少有一个参数不是c++标准类型中的,否则原有的功能已经很全,再重载毫无意义)
下面均用复数类举例:
class Complex{
private:
double real,imag ;
public:
Complex(){real=0;imag=0;}
Complex(double r,double i){real=r;imag=i;}
void display(){cout<<"("<<real<<","<<imag<<")"<<endl;}
};
1.运算符重载的格式:
重载运算符的一般格式:
函数类型 operator 运算符名称 (形参表){ 对运算符的处理 }
说明:
运算符被重载后其原有功能仍被保留,还多了重载后可以实现的功能,系统会根据运算符前后的数据类型自动选择用哪个功能。
例:想让‘+’用于复数类的加法运算
class Complex{
private:
double real,imag ;
public:
Complex(){real=0;imag=0;}
Complex(double r,double i){real=r;imag=i;}
void display(){cout<<"("<<real<<","<<imag<<")"<<endl;}
Complex operator + (Complex & c);
};
Complex Complex::operator + (Complex & c){
return Complex(real+c.real,imag+c.imag);
}
int main()
{
Complex c1(1,2),c2(3,4),c3,c4;
c3=c1+c2;
c4=c3+Complex(5,6);//临时对象
c3.display();
c4.display();
}
输出的结果均为(4,6)和(9,12)。
2.类的运算符重载有两种方式:
(1)重载为成员函数:
Complex operator + (Complex & c){
return Complex(real+c.real,imag+c.imag);
}
Complex c1(1,2),c2(3,4),c3;
c3=c1+c2;
c3.display();
(2)重载为友函数
friend Complex operator + (Complex & c1,Complex & c1){
return Complex(c1.real+c2.real,c1.imag+c2.imag);
}
Complex c1(1,2),c2(3,4),c3;
c3=c1+c2;
c3.display();
输出的结果均为(4,6)。
第一种是重载为成员函数,参数个数为运算符数目减一(因为可以通过this指针自由的访问本类的数据成员),第二种是重载为普通函数,参数个数为运算符目数,重载为友元函数是因为方便调用对象中的成员。
那么问题来了,什么时候用友元函数,什么时候用类成员函数?
首先我们来看在上面的例子中是c3=c1+c2;
,对于类成员函数的重载来说运算符两侧的参数必须是与运算符函数类型相同的类对象,如果我们想要让类的对象和其他数据类型的变量或常数相加,如c3=c2+1;
这时我们需要再次重载‘+’,因为之前重载的只能计算两个对象的加法。
Complex operator + (int & i){
return Complex(real+i,imag);
}
此时c3=c2+1;
便可以实现了。
那么c3=1+c2;
呢?显然是不行的,因为这个重载同样利用了类成员函数可以用this指针的便利,运算符左侧的参数必须是与运算符函数类型相同的类对象,这样我们还需要写一个重载函数:
friend Complex operator + (int & i,Complex & c){
return Complex(i+c.real,c.imag);
}
这样c3=1+c2;
也实现了。
重载为友元函数时,就必须在形参列表里有两个参数了,并且形参的顺序是任意的,但要注意在使用这个运算符时,运算符两侧的顺序必须对应形参的顺序!!!所以如果抛开上面那个成员函数只用友元函数重载的话,c3=c2+1;
便会出错。我们仍需要再写一个:
friend Complex operator + (Complex & c,int & i){
return Complex(i+c.real,c.imag);
}
根据上面这个例子,我们可以看出:数学上的交换律在此并不适用!!!
总结:
(1)c++规定,"="、"[]’’、’’()’’、’’->’'必须作为成员函数重载
(2)"<<" 、">>"、类型转换运算符重载时只能作为友元函数
(3)一般将单目运算符和符合运算符(+=、-=、/=、*=、&=、!=、^=、%=、>>=、<<=)重载为成员函数,双目运算符重载为友元函数。
3.++、- -(单目运算符)重载:
++、- -运算符是单目运算符,只有一个操作数,故重载为成员函数时可以省略参数。关键在于++、- -分为前置和后置,它们作用不同,重载时如何区分二者呢?
c++约定:在自增和自减运算符重载函数中增加一个int类型的形参来代表后置的自增和自减运算符重载函数。注意这个int形参并无卵用,定义时和调用时也并不用写出参数名。
下面以++为例:
class Complex{
private:
double real,imag ;
public:
Complex(){real=0;imag=0;}
Complex(double r,double i){real=r;imag=i;}
void display(){cout<<"("<<real<<","<<imag<<")"<<endl;}
Complex operator ++ (){
++real;
return *this;
}
Complex operator ++ (int ){
Complex temp(*this);//建立临时对象!!!因为后置++是先是自加前的变量参与运算,之后再++;
real++;
return temp;//返回自加前的对象!!!
}
};
int main(){
Complex a(1,2),b;
++a; a.display();
b=a++; b.display(); a.display();
}
输出结果为(2,2)(2,2)(3,2)
4.<<、>>的重载:
对于用户自己定义的类型的数据,不能用<< >>直接输出输入,我们可以重载<< >>:(只能重载为友元函数!!!因为第一个参数是o/istream类)
friend istream & operator >> (istream & input,自定义类名 & 形参名){
input>>类中想要输入的内容;
return input;//一定要返回istream的对象
}
friend ostream & operator << (ostream & output,自定义类名 & 形参名){
output<<类中想要输出的内容;
return output;//一定要返回ostream的对象
}
这样我们就可以直接写cin>>对象名;
或cout<<对象名;
来实现用<<、>>对对象的输入和输出。
5.关于运算符重载何时返回引用:
可以看到上面诸多例子中有的返回函数类型的引用而有的就是返回函数类型的变量,如果返回引用时返回的不是常量而是对象的引用,它可以出现在赋值号左侧而被赋值或参与其他运算(看下面的例子)。
下面是总结的规则:
如果运算符的返回值会被赋值就必须返回引用,如= [ ] >> <<;如果运算符返回值是一个局部变量,就一定不能返回引用,如“+”。为了提高效率,参数可以使用引用,但参数为指针时就不能用了!
举例:对于[ ]的重载(通常用于含有数组/指针的类中,重载[]便于直接利用对象来表示其数组元素)
class Array{
int size;//数组元素个数
int *ptr;//指向动态分配的数组
public:
void push_back(int v){//插入一个元素
if(ptr){//如果之前的ptr不是空的,需要另开辟空间
int *tmp=new int[size+1];
memcpy(tmp,ptr,sizeof(int)*size);//把ptr的拷贝到tmp中
delete [] ptr;
ptr=tmp;
}else{
ptr=new int[1];
}
ptr[size+1]=v;
}
int & Array::operator [] (int i){ return ptr[i];}
};
int main(){
Carry a;
for(int i=0;i<5;i++) a.push_back(i);
a[3]=6;
for(int i=0;i<5;i++) cout<<a[i]<<" ";
}
输出结果为1 2 3 6 5。
比较特殊的就是=和&了,因为=已经可以直接用于对象间的赋值(系统自带),只有在默认的=不能实现我们的需求或者可能出错(如空间的分配,如指针成员)才需要自己重载=;&是地址运算符用于对象就是返回其在内存中的地址
6.类型转换函数:
前面我们看过了类型转换构造函数可以将一个其他类型的数据转换为类的一个对象,这里的类型转换函数是为了把类的对象转换成其他类型的数据。
基本格式:
operator 其他类型名 (){
实现转换的语句;
}
类型转换函数只能作为成员函数,因为调用的是this指针。
例:
class Complex{
private:
double real,imag ;
public:
Complex(){real=0;imag=0;}
Complex(double r,double i){real=r;imag=i;}
void display(){cout<<"("<<real<<","<<imag<<")"<<endl;}
operator double () { return real; }//类型转换函数
};
int main(){
Complex a(1,2);
double (a);
cout<<a;//输出结果为1
double b;
b=2.5+a;
cout<<b;//输出结果为3.5
}
在前面我们就是用‘+’的重载来进行举例的,其中c+2和2+c是需要不同的重载来实现的,而如果我们结合使用转换构造函数并把“+”重载为友元函数,在进行对象相加时就可以符合交换律。但是不能结合类型转换函数!!!,因为系统不知道c+2是应该把c转换成double,还是把2转换成Complex对象而出现歧义。
根据上面的分析我们可以看出如果需要让对象与其他类型的变量进行运算时,类型转换函数可以避免我们对所有需要用到对象运算的运算符进行重载,而是让系统自动转换为需要的类型并直接用系统提供的运算符运算,很方便。
7.重载运算符的规则:
把规则写到最后是因为只有充分用实例体会了其中的奥妙才能理解出规则的真谛。
(1)不能重载的运算符:’.’(成员访问运算符)、‘*’(指针)、::(域)、sizeof、?:(条件运算符);
(2)重载不能改变运算符目数(操作数)–>重载运算符的函数不能有默认参数;
(3)重载不能改变运算符的优先级别和结合性;
(4)重载的运算符必须和至少一个用户自定义类型的对象一起使用,否则就失去了重载的意义。
(5)用于类的运算符除了=、&(取地址)之外(为什么看5.)基本都要重载。而且理论上重载的运算符可以实现任意运算,但还是要重载为和运算符本身功能类似的,否则别人看到>用于实现<的作用那就很捞了。
其他再补充,大多都在其他点里面体会了。
总结:
看课件和书写了这些,其实我们真正用到运算符重载无外乎就是想直接对类的对象进行操作,而不是去通过调用那些繁琐的成员函数再调用私有成员。我们只需要根据需求进行重载即可。
最后给一道题来总结上面几乎所有不同类型的重载方式,不要嫌多,主要是需求多emmm…
仔细体会:
程序填空,使输出符合给出的输出
输入
无
输出
1. abcd-efgh-abcd-
2. abcd-
3.
4. abcd-efgh-
5. efgh-
6. c
7. abcd-
8. ijAl-
9. ijAl-mnop
10. qrst-abcd-
11. abcd-qrst-abcd- uvw xyz
about
big
me
take
abcd
qrst-abcd-
#include <cstdlib>
#include <iostream>
using namespace std;
int strlen(const char * s)
{ int i = 0;
for(; s[i]; ++i);
return i;
}
void strcpy(char * d,const char * s)
{
int i = 0;
for( i = 0; s[i]; ++i)
d[i] = s[i];
d[i] = 0;
}
int strcmp(const char * s1,const char * s2)
{
for(int i = 0; s1[i] && s2[i] ; ++i) {
if( s1[i] < s2[i] )
return -1;
else if( s1[i] > s2[i])
return 1;
}
return 0;
}
void strcat(char * d,const char * s)
{
int len = strlen(d);
strcpy(d+len,s);
}
class MyString
{
// 在此处补充你的代码
};
int CompareString( const void * e1, const void * e2)
{
MyString * s1 = (MyString * ) e1;
MyString * s2 = (MyString * ) e2;
if( * s1 < *s2 )
return -1;
else if( *s1 == *s2)
return 0;
else if( *s1 > *s2 )
return 1;
}
int main()
{
MyString s1("abcd-"),s2,s3("efgh-"),s4(s1);
MyString SArray[4] = {"big","me","about","take"};
cout << "1. " << s1 << s2 << s3<< s4<< endl;
s4 = s3;
s3 = s1 + s3;
cout << "2. " << s1 << endl;
cout << "3. " << s2 << endl;
cout << "4. " << s3 << endl;
cout << "5. " << s4 << endl;
cout << "6. " << s1[2] << endl;
s2 = s1;
s1 = "ijkl-";
s1[2] = 'A' ;
cout << "7. " << s2 << endl;
cout << "8. " << s1 << endl;
s1 += "mnop";
cout << "9. " << s1 << endl;
s4 = "qrst-" + s2;
cout << "10. " << s4 << endl;
s1 = s2 + s4 + " uvw " + "xyz";
cout << "11. " << s1 << endl;
qsort(SArray,4,sizeof(MyString),CompareString);
for( int i = 0;i < 4;i ++ )
cout << SArray[i] << endl;
//s1的从下标0开始长度为4的子串
cout << s1(0,4) << endl;
//s1的从下标5开始长度为10的子串
cout << s1(5,10) << endl;
return 0;
}
//其实就是让我们写一个类来实现上面所有的运算,其实就是让我们实现string类的一部分功能:
class MyString
{
// 在此处补充你的代码
public:
MyString() {}
MyString(char *c):s(c){}
MyString(const MyString & c){
strcpy(s,c.s);
}
friend ostream & operator << (ostream & output,MyString & c){
output<<c.s;
return output;
}
char & operator [] (int i) {
return s[i];
}
MyString & operator = (char *c){
delete [] s;
s=new char[strlen(c)+1];
strcpy(s,c);
return *this;
}
MyString & operator = (MyString & c){
delete [] s;
s=new char[strlen(c.s)+1];
strcpy(s,c.s);
return *this;
}
MyString & operator += (char *c){
delete [] s;
s=new char[strlen(c)+strlen(s)+2];
strcat(s,c);
return *this;
}
friend MyString operator + (char *c,MyString & d){
strcat(d.s,c);
MyString e(d);
return e;
}
friend MyString operator + (MyString & c,MyString & d){
strcat(c.s,d.s);
MyString e(c);
return e;
}
MyString operator + (char *c){
strcat(s,c);
return *this;
}
char* & operator () (int i,int len) {
char *tmp;
tmp=new char[len+1];
for(int k=i,j=0;k<i+len;k++,j++){
tmp[j]=s[k];
}
return tmp;
}
private:
char *s;
};
上面的重载真的多,但是只要根据要求的输出和函数、main耐心一步步找需求即可,我们的指导思想:先搭框架,逐步扩充,由简到繁,最后完善,边编程,边调试。
derbi123123 发布了96 篇原创文章 · 获赞 0 · 访问量 1178 私信 关注内容总结
以上是互联网集市为您收集整理的c++面向对象程序设计学习总结之运算符重载全部内容,希望文章能够帮你解决c++面向对象程序设计学习总结之运算符重载所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。