-
運算符重載的基本概念
為什麼要重載運算符?
C++中預定義的運算符隻能用于基本資料類型的計算,但有時我們會希望對象也能通過運算符進行運算,(比如求兩個複數對象的和)這樣會使代碼更簡潔,容易了解。
運算符重載,就是對已有的運算符(C++中預定義的運算符)賦予多重的含義,使同一運算符作用于不同類型的資料時導緻不同類型的行為。
重載的目的是擴充C++中提供的運算符的适用範圍,使之能作用于對象。
同一個運算符,對不同類型的操作數,所發生的行為不同。
例如:
complex_a + complex_b 生成新的複數對象
5 +4 = 9
運算符重載的實質是函數的重載。
可以重載為普通函數,也可以重載為成員函數。
把含運算符的表達式轉換成對運算符函數的調用。
把運算符的操作數轉換成運算符函數的參數。
運算符被多次重載時,根據實參的類型決定調用哪個運算符函數。
運算符重載的形式:
傳回值類型 operator 運算符(形參表)
{
······
}
運算符重載的示例:
class Complex
{
public:
double real, imag;
Complex (double r=0.0, double i=0.0):real(r),imag(i){ };
Complex operator-(const Complex & c);
};
Complex operator+(const Complex & a, const Complex & b)
{
return Complex(a.real+b.real, a.imag+b.imag); //傳回一個臨時對象
}
Complex Complex::operator-(const Complex & c)
{
return Complex(real-c.real, imag-c.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-b等價于a.operator-(b)
return 0;
}
重載為成員函數時,參數個數為運算符目數減一;
重載為普通函數時,參數個數為運算符目數。
輸出結果為:
5,5
3,3
-
指派運算符‘=’的重載
有時候希望指派運算符兩邊的類型可以不比對,比如:把一個int類型的變量指派給一個Complex對象,或把一個char*類型的字元串指派給一個字元串對象,此時就需要重載指派運算符“=”。
指派運算符隻能重載為成員函數。
class String {
private:
char * str;
public:
String ():str(new char[1]) { str[0] = 0;}
const char * c_str() { return str; };
String & operator = (const char * s);
~String( ) { delete [] str; }
};
String & String::operator = (const char * s)
{ //重載“=”以使得 obj = “hello”能夠成立
delete [] str;
str = new char[strlen(s)+1];
strcpy( str, s);
return * this;
}
int main()
{
String s;
s = "Good Luck," ; //等價于 s.operator=("Good Luck,");
cout << s.c_str() << endl;
// String s2 = "hello!"; //這條語句要是不注釋掉就會出錯
s = "Shenzhou 8!"; //等價于 s.operator=("Shenzhou 8!");
cout << s.c_str() << endl;
return 0;
}
-
運算符重載為友元函數
一般隻需将運算符重載為類的成員函數便是較好的選擇,但有時,重載為成員函數不能滿足使用要求,重載為普通函數,又不能通路類的私有成員,是以才将運算符重載為友元。
引出代碼:
class Complex
{
double real,imag;
public:
Complex( double r, double i):real(r),imag(i){ };
Complex operator+( double r );
};
Complex Complex::operator+( double r )
{ //能解釋 c+5
return Complex(real + r,imag);
}
經過上述重載後,能夠解釋c+5,但是不能解釋5+c
是以改動重載函數為:
Complex Complex::operator+(double r, const Complex & c)
{
return Complex(c.real+r, c.imag);
}
上述函數就可以解釋5+c的問題了,但是普通函數是不能通路私有成員的,需要将運算符+重載為友元。
class Complex
{
double real,imag;
public:
Complex( double r, double i):real(r),imag(i){ };
Complex operator+( double r );
friend Complex operator + (double r,const Complex & c);
};
-
可變長整型數組(運算符重載的執行個體)
#include <iostream>
#include <string>
#define _SCL_SECURE_NO_WARNINGS
using namespace std;
class CArray {
int size;// 數組元素的個數
int *ptr;//指向動态配置設定的數組
public:
CArray(int s = 0);//s代表數組元素的個數
CArray(CArray & a);
~CArray();
void push_back(int v);//用于在數組尾部添加一個元素
CArray &operator=(const CArray & a);//用于數組對象間的指派
int length() { return size; }//傳回數組元素的個數
int & operator[](int i)//傳回值為int是不行的,不支援a[i]=4.
{
return ptr[i]; //用于支援根據下标通路數組元素,如n=a[i]和a[i]=4這樣的語句。
}
};
CArray::CArray(int s) :size(s) {
if (s == 0) {
ptr = NULL;
}
else {
ptr = new int[s];
}
}
CArray::CArray(CArray & a) {
if (!a.ptr) {
ptr = NULL;
size = 0;
return;
}
ptr = new int[a.size];
memcpy(ptr, a.ptr, sizeof(int)*a.size);
size = a.size;
}
CArray::~CArray() {
if (ptr)
delete[] ptr;
}
CArray & CArray::operator=(const CArray & a) {
//指派号重載的作用是使“=”左邊的對象裡存放的數組,大小和内容都和右邊的對象一樣
if (ptr == a.ptr) { //防止a = a這樣的指派導緻出錯
return *this;
}
if (a.ptr == NULL) { //a裡面的數組是空的
if (ptr) {
delete[] ptr;
}
ptr = NULL;
size = 0;
return *this;
}
if (size < a.size) {//如果原有空間夠大,就不用配置設定新的空間
if (ptr) {
delete[] ptr;
}
ptr = new int[a.size];
}
memcpy(ptr, a.ptr, sizeof(int)*a.size);
size = a.size;
return *this;
}
void CArray::push_back(int v) {
if (ptr) {
int *temptr = new int[size + 1];//重新配置設定空間
memcpy(temptr, ptr, sizeof(int)*size);//拷貝原數組的内容
delete[] ptr;
ptr = temptr;
}
else//數組本來是空的
ptr = new int[1];
ptr[size++] = v; //加入新的數組元素
}
int main() {
CArray a; //開始的數組内是空的
for (int i = 0; i < 5; ++i) {
a.push_back(i);
}
CArray a2, a3;
a2 = a;
for (int i = 0; i < a.length(); ++i) {
cout << a2[i] << " ";
}
a2 = a3; //a2是空的
for (int i = 0; i < a2.length(); ++i) { //a2.length()傳回0
cout << a2[i] << " ";
}
cout << endl;
a[3] = 100;
CArray a4(a);
for (int i = 0; i < a4.length(); ++i) {
cout << a4[i] << " ";
}
return 0;
}
-
流插入運算符和 流提取運算符的重載
問題的引入:
cout << 5 << “this”; 為什麼能夠成立?
cout是什麼? “<<” 為什麼能用在 cout上?
cout 是在 iostream 中定義的,ostream 類 的對象。
“<<” 能用在cout 上是因為,在iostream 裡對 “<<” 進行了重載。
怎麼重載才能使得 cout << 5 << “this” ; 成立?
重載的形式:
ostream & ostream::operator<<(int n)
{
…… //輸出n的代碼
return * this;
}
ostream & ostream::operator<<(const char * s )
{
…… //輸出s的代碼
return * this;
}
問題:假定下面程式輸出為5hello,該補寫些什麼?
class CStudent{
public:
int nAge;
};
int main(){
CStudent s ;
s.nAge = 5;
cout << s <<"hello";
return 0;
}
可以分析得到上面的程式缺少對流插入運算符的重載,
即:
ostream & operator<<( ostream & o,const CStudent & s){
o << s.nAge ;
return o;
}
例題:
假定c是Complex複數類的對象,現在希望 寫“cout >c;”,就能從鍵 盤接受“a+bi”形式的輸入,并且使得 c.real = a,c.imag = b。
int main() {
Complex c;
int n;
cin >> c >> n;
cout << c << "," << n;
return 0;
}
//使得程式的輸出結果是
// 13.2+133i 87 (輸入)
// 13.2+133i, 87
顯然需要我們補充對<<和>>運算符的重載。
完整代碼如下:
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
class Complex {
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"; //以"a+bi"的形式輸出
return os;
}
istream & operator>>( istream & is,Complex & c)
{
string s;
is >> s; //将"a+bi"作為字元串讀入, “a+bi” 中間不能有空格
int pos = s.find("+",0);
string sTmp = s.substr(0,pos); //分離出代表實部的字元串
c.real = atof(sTmp.c_str()); //atof庫函數能将const char*指針指向的内容轉換成 float
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;
}
選擇題:
重載“<<”用于将自定義的對象通過cout輸出時, 以下說法哪個是正确的?
A) 可以将"<<"重載為 ostream 類的成員函數, 傳回值類型是 ostream &
B) 可以将"<<"重載為全局函數,第一個參數以 及傳回值,類型都是 ostream
C) 可以将"<<"重載為全局函數,第一個參數以 及傳回值,類型都是 ostream &
D) 可以将"<<"重載為 ostream 類的成員函數, 傳回值類型是 ostream
正确答案:C。
-
類型轉換運算符的重載
例如:
#include <iostream>
using namespace std;
class Complex
{
double real,imag;
public:
Complex(double r=0,double i=0):real(r),imag(i) { };
operator double () { return real; }
//重載強制類型轉換運算符 double
};
int main()
{
Complex c(1.2,3.4);
cout << (double)c << endl; //輸出 1.2
double n = 2 + c; //等價于 double n=2+c.operator double()
cout << n; //輸出 3.2
}
上面代碼中的double就是屬于類型轉換運算符的重載,可以使得類的對象顯示或隐式的發生轉換。
-
自增,自減運算符的重載
自增運算符++、自減運算符--有前置/後置之分,為了區分所重載的是前 置運算符還是後置運算符,C++規定:
前置運算符作為一進制運算符重載
重載為成員函數: T & operator++();
T & operator--();
重載為全局函數: T1 & operator++(T2);
T1 & operator—(T2);
後置運算符作為二進制運算符重載,多寫一個沒用的參數:
重載為成員函數: T operator++(int);
T operator--(int);
重載為全局函數: T1 operator++(T2,int );
T1 operator—( T2,int);
在沒有後置運算符重載而有前置重載的情況下,在vs中,obj++也調用前置重載,而Dev則會編譯出錯。
例題:
下面的代碼,該如何編寫CDemo類?
int main()
{
CDemo d(5);
cout << (d++ ) << ","; //等價于 d.operator++(0);
cout << d << ",";
cout << (++d) << ","; //等價于 d.operator++();
cout << d << endl;
cout << (d-- ) << ","; //等價于 operator--(d,0);
cout << d << ",";
cout << (--d) << ","; //等價于 operator--(d);
cout << d << endl;
return 0;
}
//輸出結果為
5,6,7,7
7,6,5,5
正确的代碼補充為:
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 k )
{ //後置 ++
CDemo tmp(*this); //記錄修改前的對象
n ++;
return tmp; //傳回修改前的對象
} // s++即為: s.operator++(0);
CDemo & operator--(CDemo & d)
{//前置--
d.n--;
return d;
} //--s即為: operator--(s);
CDemo operator--(CDemo & d,int)
{//後置--
CDemo tmp(d);
d.n --;
return tmp;
} //s--即為: operator--(s, 0);
-
運算符重載的注意事項
1. C++不允許定義新的運算符 ;
2. 重載後運算符的含義應該符合日常習慣; complex_a + complex_b word_a > word_b date_b = date_a + n ‘’
3. 運算符重載不改變運算符的優先級;
4. 以下運算符不能被重載:“.”、“.*”、“::”、“?:”、sizeof;
5. 重載運算符()、[]、->或者指派運算符=時,運算符重載函數必須聲明為 類的成員函數