OOP筆記 (六) 運算符重載
1.運算符重載
對抽象數據類型也能夠直接使用C++提供的運算符
• 程序更簡潔
• 代碼更容易理解
例如:
• complex z1和complex z2是兩個複數對象,求兩個複數的和, 希望能直接寫:z1 + z2
• 字符串 MyString str1和MyString str2,兩字符串拼接
str1+str2
2.函數定義說明:
(1)函數類型指定重載運算符的返回值類型(即運算結果類型);
(2)operator定義運算符重載函數的關鍵字;
(3)運算符指定要重載的運算符的名稱,例如+ - [];
(4)形參表中給出重載運算符所需要操作對象的參數和類型。
例如:3+4中+號對應的操作數類型是int
“hello”+ “world” = “helloworld”對應的類型是MyString
例子:實現複數的加減
運算符重載爲普通函數:
class Complex
{
public:
Complex(double r=0.0,double i=0.0)
{real=r;image=i;}
friend Complex operator+(const Complex & a, const Complex & b);
//+:運算符重載爲普通函數,如需訪問私有成員,必須設置爲友元函數
private:
double real;
double image;
};
Complex operator+ (const Complex & a, const Complex & b)
{
return Complex( a.real+b.real, a.image+b.image);
}// (1)形參類型和個數
int main()
{
Complex z1(10,20),z2(5,7),z;
z=z1+z2; // (2)調用寫法
return 0;
}
+:運算符重載爲成員函數:
//(2)運算符+號重載爲成員函數
Complex Complex::operator +(const Complex &a)
{
return Complex(real+a.real,image+a.image);
}
<< :輸出運算符重載:
//(3)<<輸出運算符重載
ostream& operator<<(ostream& out, const Complex & a)
{
out<<a.real<<":"<<a.image<<endl;
return out;
}
【注意】(1)運算符左邊是輸出標準對象(第一個操作數),右邊是輸出內容(第二個操作數),只能重載爲友元函數
(2)返回值滿足輸出運算符能做拼接的要求,參數是引用,返回類型是引用
>> :輸入運算符重載:
//(4)>> 輸入運算符重載
istream& operator>>(istream& in, Complex & a)
{
in>>a.real>>a.image;
return in;
}
放在一起:
class Complex
{
public:
Complex(double r=0.0,double i=0.0)
{real=r;image=i;}
void display();
//(1) '+'運算符重載爲友元函數
friend Complex operator+ (const Complex & a, const Complex & b);
//(2)運算符+號重載爲成員函數
// Complex operator +(const Complex &a);
//(3)<<輸出運算符重載
friend ostream& operator<<(ostream& out, const Complex & a);
friend istream& operator>>(istream& in, Complex & a);
private:
double real;
double image;
};
//(1)'+'運算符重載爲友元函數
Complex operator+ (const Complex & a, const Complex & b)
{
return Complex( a.real+b.real, a.image+b.image);
}
//(2)運算符+號重載爲成員函數
//Complex Complex::operator +(const Complex &a)
//{
// return Complex(real+a.real,image+a.image);
//}
//(3)<<輸出運算符重載
ostream& operator<<(ostream& out, const Complex & a)
{
out<<a.real<<":"<<a.image<<endl;
return out;
}
//(4)>> 輸入運算符重載
istream& operator>>(istream& in, Complex & a)
{
in>>a.real>>a.image;
return in;
}
int main()
{
Complex z1(10,20),z2,z;
cin>>z2;
z=z1+z2;
cout<<z;
return 0;
}
自加 ++/自減–-:
#include <iostream>
#include<cstdio>
using namespace std;
class CDemo
{
public:
CDemo(int x1):x(x1){}
CDemo &operator ++() //(1)前置++:重載爲成員函數
{
++x; //前置++運算結果滿足左值,返回類型必須是引用
return *this; //返回當前對象的值,this表示對象的地址
}
friend CDemo operator ++(CDemo &a,int t);
friend ostream & operator<<(ostream &out,const CDemo &d1);
private:
int x;
};
CDemo operator ++(CDemo &a,int t) //(2)後置++:重載爲友元函數
{
CDemo old=a;
a.x++;
return old; //後置++運算結果返回值,返回類型不是引用
}
ostream & operator<<(ostream &out,const CDemo &d1) //(3)<<:輸出運算符重載
{
out<<d1.x;
return out;
}
3.運算符重載爲友元函數
運算符也重載爲類的友元函數,這樣,它就可以自由訪問該類的任何數據成員。這時,運算所需要的操作數都需要通過函數的形參表來傳遞。
將運算符重載爲友元函數的情況:
• 成員函數不能滿足使用要求
• 普通函數, 又不能訪問類的私有成員
運算符左側的操作數與函數第一個參數對應,右側的和第二個參數對應。
當重載友元函數時,將沒有隱含的參數 this 指針。這樣,
對雙目運算符,友元函數有兩個參數,
對單目運算符,友員函數有一個參數。
4.運算符重載的注意事項
(1)C++不允許定義新的運算符,重載後運算符的含義應符合日常習慣
complex_a + complex_b
word_a > word_b
(2)重載後運算符的優先級和結合性都不會改
(3)一般重載的功能應當與原有功能相類似(運算符重載主要是針對新類型數據的實際需要,而對原有運算符進行適當的改造),不能改變原來運算符的操作對象個數,同時至少要有一個操作對象是自定義類型。
(4)重載運算符(), [], ->或者賦值運算符=時, 重載函數必須聲明爲類的成員函數
5.特殊運算符重載
1、=: 賦值運算符
賦值運算符 兩邊的類型 可以 不匹配
(1)把一個 int類型變量 賦值給一個 Complex對象
(2)把一個 char * 類型的字符串 賦值給一個 字符串對象
需要 重載賦值運算符 ‘=’
賦值運算符 ‘=’只能重載爲 成員函數
(1)淺拷貝存在問題
有兩個致命的錯誤
①當我們通過s2修改它的str時,s1的str同時被修改!
②當執行s2和s1的析構函數時,會導致同一內存區域釋放兩次,程序崩潰!
(2) 深複製/深拷貝(顯示定義賦值運算符重載): 將一個對象中指針變量指向的內容,複製到另一個對象中指針成員對象指向的地方
//(1)= :深複製,參數是const MyString&
MyString& MyString::operator = (const MyString& s)
{
if(str)delete [] str;
if(s.str)
{
int i;for(i=0;s.str[i];i++);
str=new char[i+1];
for(i=0;s.str[i];i++) str[i]=s.str[i];
str[i]='\0'; //strcpy()
}
else str=NULL; return *this;
}
2、 []:下標運算符重載
運算符代碼實現:
#include<iostream>
using namespace std;
class MyString
{
private:
char * str;
public:
MyString():str(NULL) { } //構造函數, 初始化str爲NULL
const char * c_str() { return str; }//返回str成員內容
//(1)= :賦值運算符重載,參數類型是const 字符串
MyString& operator = (const char * s);
//(1)= :賦值運算符重載,參數是const MyString&
MyString& operator = (const MyString& s);
//(3)深拷貝:複製構造函數
MyString(MyString &s);
//(4)強制轉換
operator char *()
{
return str;
}
int size()
{
int i;
for(i=0;str[i];i++);
return i;
}//(2)[]: 下標運算符重載
char &operator[](int i);
~MyString( );
};
//(1)= :賦值運算符重載
MyString& MyString::operator = (const char * s)
{
if(str)delete [] str;
if(s)
{ int i;for(i=0;s[i];i++);
str=new char[i+1];
for(i=0;s[i];i++) str[i]=s[i];
str[i]='\0';
}
else str=NULL; return *this;
}
//(3)深拷貝:複製構造函數
MyString::MyString(MyString& s)
{
if(s.str)
{
int i;for(i=0;s.str[i];i++);
str=new char[i+1];
for(i=0;s.str[i];i++) str[i]=s.str[i];
str[i]='\0'; //strcpy()
}
else str=NULL;
}
//(1)= :深複製,參數是const MyString&
MyString& MyString::operator = (const MyString& s)
{
if(str==s.str) return *this;
if(str)delete [] str;
if(s.str)
{
int i;for(i=0;s.str[i];i++);
str=new char[i+1];
for(i=0;s.str[i];i++) str[i]=s.str[i];
str[i]='\0';
}
else str=NULL; return *this;
}
//(2)[]: 下標運算符重載
char & MyString::operator[](int i)
{
if(i<0||i>=size())
throw "index is below or exceed";
else return str[i];
}
MyString::~MyString()
{
if(str)
delete []str;
}
int main()
{
MyString s1,s2;
s1="this";
cout<<(char *)s1<<endl;
s1=s1;
s2="that";
MyString s3=s2;
cout<<(char *)s1<<endl;
cout<<s3.c_str()<<endl;
}