重載不能改變操作符的優(yōu)先級
如果一個內(nèi)建操作符是一元的,那么所有對它的重載仍是一元的。如果是二元的重載后也是二元的
下面看一個有代表性的例子:: 頭文件Complex.h: #include <iostream> class Complex { void write() const; Complex::Complex(){ Complex::Complex(double re) { Complex::Complex(double re,double im) { void Complex::write() const { Complex Complex::operator + (const Complex &u) const { Complex Complex::operator - (const Complex &u) const { Complex Complex::operator* (const Complex &u) const { Complex Complex::operator / (const Complex &u) const {
測試文件:Complex.cpp和測試結(jié)果:
一個被重載的操作符,就是一個用戶自定義的函數(shù),只不過它可以享受操作符語法所帶來的便利
除了內(nèi)存管理操作符new、new[]、delete、delete[]之外,一個以頂層函數(shù)形式被重載的操作符必須在它的參數(shù)列表中包含一個類的對象 下標(biāo)操作符[]、賦值操作符=、函數(shù)調(diào)用操作符()和指針操作符->必須以類成員函數(shù)的形式進(jìn)行重載(這樣可以保證第一個操作數(shù)是類的對象,不然9[x]、6.32=x,不被接受) 操作符(如%)要么以成員函數(shù)被重載,要么以頂層函數(shù)被重載。對于后者將至少帶一個對象參數(shù),這是因為操作符以頂層函數(shù)實現(xiàn)時,如果連一個參數(shù)都沒有,那么對如下表達(dá)式 x % y 編譯系統(tǒng)就不能區(qū)分%是內(nèi)建的,還是用戶的。如果操作符%是類成員函數(shù),或是有一個類對象參數(shù)的頂層函數(shù),編譯系統(tǒng)就能夠根據(jù)特定的上下文決定調(diào)用哪一個%操作符
頂層函數(shù)重載操作符,類名和域解析符都沒有了,因為它不是一個類成員函數(shù)
被重載的操作符,要么是一個類成員函數(shù),要么在它的參數(shù)列表中包含一個類成員!!
下標(biāo)操作符[]和函數(shù)調(diào)用操作符()只能以成員函數(shù)的形式被重載,不能成為頂層函數(shù)!! eg:
修正后:
在上面那種情況,如果想要第二個表達(dá)式成功的話,就需要定義一個頂層函數(shù)重載+了
有一點(diǎn)要明白,當(dāng)定義頂層函數(shù)時,函數(shù)里面不能含有對象的私有成員操作,否則編譯通不過,下面有三種方法可以解決: 1,將私有成員設(shè)計為public成員,但是這種方法違背了類的信息隱藏原則 2,在Complex中加入用于訪問real和imag的公有成員函數(shù),但是容易造成接口混淆 3,將操作符重載函數(shù)聲明為類的friend,但是不符合面向?qū)ο笤瓌t,應(yīng)少用,建議僅在操作符重載時使用
類的私有成員只能被該類的成員函數(shù)和該類的friend函數(shù)訪問 類的保護(hù)成員只能被該類的或其派生類的成員函數(shù)和該類的friend函數(shù)訪問
class C { //.... friend int f(); //... }; 該聲明賦予f訪問C的私有和保護(hù)成員的權(quán)力,因為f不是成員函數(shù),該聲明可以放在C中的private、protected或public的任意部分,不受訪問控制符的影響、限制
程序員可對操作符>>進(jìn)行重載,以支持用戶自定義數(shù)據(jù)類型。>>的第一個操作數(shù)是系統(tǒng)類的對象(如cin是系統(tǒng)類istream的對象),而這些重載函數(shù)時以類成員函數(shù)的形式實現(xiàn)的。 如果在對用戶自定義的類型重載>>時,就必須對系統(tǒng)類的源代碼進(jìn)行修改,而這顯然是非常不明智的做法,因此只能將>>重載函數(shù)設(shè)計為頂層函數(shù)
輸入流對象總是以引用方式傳遞,這是因為系統(tǒng)為了接受輸入數(shù)據(jù),需要更新輸入流對象的某些信息 如果被重載>>的函數(shù)中和類的私有數(shù)據(jù)或者保護(hù)數(shù)據(jù)打交道,則需要將重載操作符聲明為friend
拷貝構(gòu)造函數(shù)和賦值操作符(=),都是用來拷貝一個類的對象給另一個相同類型的對象。 拷貝構(gòu)造函數(shù)將一個對象拷貝到另一個新的對象(因為拷貝-構(gòu)造);賦值操作符將一個對象拷貝到另一個已經(jīng)存在的對象
如果類的作者沒有提供拷貝構(gòu)造函數(shù),也沒有重載賦值操作符,編譯器將會給這個類提供一個拷貝構(gòu)造函數(shù)和一個賦值操作符。編譯器提供的拷貝構(gòu)造函數(shù)和賦值操作符的運(yùn)轉(zhuǎn)機(jī)制是:將源對象中的每個數(shù)據(jù)成員拷貝到目標(biāo)對象相應(yīng)的數(shù)據(jù)成員中
看例子:
如果類的作者定義了指針成員,且該指針指向一塊動態(tài)分配的存儲空間,就應(yīng)該為這個類設(shè)計拷貝構(gòu)造函數(shù),并重載賦值操作符 注意不要返回臨時變量,重載里面錯綜復(fù)雜,小心一點(diǎn)
下標(biāo)操作符[]必須要以成員函數(shù)的形式進(jìn)行重載! class C { //可以修改對象 returntype & operator[] (paramtype); }; 或者: class C { //不能修改對象 const returntypr & operator[] (paramtype) const; }; 例子: 頭文件:test.h
測試文件和結(jié)果:
上面可以不提供const版本的重載,但是就不能處理非const的對象,所以,有時候要考慮這一點(diǎn),加上const重載!
函數(shù)調(diào)用操作符()必須要以成員函數(shù)的形式重載。 class C { returntype operator()(paramtypes); };
看例子: 頭文件inttwoarray.h #include <iostream> class intTwoArray { int & intTwoArray::operator() (int i,int j) { const int & intTwoArray::operator() (int i,int j) const { intTwoArray::intTwoArray(int s1,int s2) { 測試程序和結(jié)果:
自增,自減也可以重載,但是前置,后置一共四種,前后置通過一個int參數(shù)區(qū)分,但是這個參數(shù)沒有什么實際用途,僅僅起到區(qū)分的作用 例子: 頭文件clock.h (需要注意一點(diǎn),這個地方注意注釋部分中間的代碼,一定要加上否則。。。。。) #include <iostream> //********************************************************** class Clock { Clock::Clock(int h,int m,int ap_flag) { Clock Clock::tick() { Clock Clock::operator++() { Clock Clock::operator++(int n) { ostream & operator<<(ostream& out,const Clock &c) { 測試程序1和結(jié)果:
測試程序2和結(jié)果:
轉(zhuǎn)型構(gòu)造函數(shù)可以將其他類型轉(zhuǎn)換成所需的類的類型。如果要進(jìn)行相反的轉(zhuǎn)型動作,即要將類的類型轉(zhuǎn)換成其他類型,可以對轉(zhuǎn)型操作符進(jìn)行重載: operator othertype(); <====== 相對=====>轉(zhuǎn)型構(gòu)造函數(shù) 注意: 聲明中不能包含返回類型,即使void也不行,但函數(shù)體中必須包含return語句,用來返回轉(zhuǎn)型結(jié)果
雖然轉(zhuǎn)型操作符重載函數(shù)帶來很多便利,但仍需謹(jǐn)慎使用,因為編譯器嘗嘗在需要的時候調(diào)用轉(zhuǎn)型操作符重載函數(shù),而這些隱式的調(diào)用對程序員來說,是不可見的,特別是當(dāng)程序員并不希望發(fā)生這種調(diào)用時會產(chǎn)生無法預(yù)料的后果
看例子: 頭文件dict.h: (注意該頭文件有一個問題,為什么friend不能對類的參數(shù)取值?。。”仨毎裦riend用的參數(shù)定義的外面?。。。?/span>
#include <iostream>
class Entry {
void Entry::operator=(const string &str) {
void Entry::operator=(const char * str) {
ostream & operator<<(ostream &out,const Entry &e) {
void Entry::add(const string &w,const string &d) {
bool Entry::match(const string &key) const {
enum {MaxEntries = 100};
class Dict {
};
ostream & operator<<(ostream &out,const Dict &d) {
Entry &Dict::operator[](const string &k) { //返回引用是該程序的技巧。因為程序通過這個地方,和前面重載的=操作符填充字典返回因為是為了修改對應(yīng)的值,即賦值
Entry & Dict::operator[](const char *k) {
void Dict::remove(const string &w)
測試程序和結(jié)果:
內(nèi)存管理操作符new、new[]、delete和delete[]既可以用成員函數(shù)也可以用頂層函數(shù)重載 嵌入式內(nèi)存有限,應(yīng)用程序經(jīng)常需要直接管理內(nèi)存
new操作符重載: void * C::operator new(size_t size) { //... } 和 void * operator new(size_t size) { //... } 返回值void * new和new[]操作符重載函數(shù)的第一個參數(shù)必須是size_t類型,其數(shù)值等于被創(chuàng)建對象大小,其他參數(shù)是可選的
delete操作符重載: void C::operator delete( void * objPtr) { //... } 和 void operator delete(void *objPtr) { //... }
delete和delete[]操作符重載函數(shù)的第一個參數(shù)必須是void*類型,返回值必須是void,而其他參數(shù)則是可選的 看例子: 頭文件frame.h: #include <iostream> const int MaxFrames = 4; class Frame { void delete(void *); Frame * allFrames = 0; Frame::Frame(const string &n,const void *d, unsigned bsize) { void Frame::print() const { void * Frame::operator new(size_t size) { void Frame::operator delete(void *adr) { int i = static_cast<unsigned char *>(adr) - framePool; i /= sizeof(Frame); alloc[i] = false; }
測試程序和結(jié)果:
一個類中的成員函數(shù)可以使另一個類的friend:
常見編程錯誤: 1,賦值操作符是唯一一個不能被繼承的操作符 2,除了內(nèi)存管理操作符,所有的操作符要么以類成員函數(shù)形式重載,要么其參數(shù)列表中至少有一個類的對象 3,[],(),=和->必須以成員函數(shù)重載 4,如果一個一元操作符以頂層函數(shù)重載,必定有一個參數(shù) |
|