C++ class without pointer
- 寫在前面
終於放假了,可以自由安排時間了,既然已經決定放棄做實驗,不是不想堅持,更想正常畢業,可是這個實驗偶然性太大,最主要的是不可重現,所以也沒意義,也沒有理論支持,當然角度新,如果測量出一個好 的實驗效果,或許我也通過答辯而順利畢業,可我不想為了畢業而畢業,再說都做了一年多了,花了大量時間在上面,過去幾個月一直焦心畢業問題,投入的時間更多,還放棄了秋招,現在,想想實在是不明智的選擇,現在,我只想做我自己想做的事,be a game changer.從今天晚上開始,我先簡單復習C++,雖然時隔比較久了,之前也只是簡單的過了過基礎,我想我還是可以的。還是從基礎開始,我堅一切知識,基礎都是非常重要的,勿在浮沙築高臺。
- Object Oriented
class 的分類:帶指針的class和不帶指針的class,
class 的聲明
這裏有一個inline的概念,寫在類裏面的默認為inline,寫在class外部的只有聲明,為inline才有可能編譯成Inline,最終編譯時是否為inline,由編譯器決定。Inline函數速度快.
public:
private:所有數據都應放在private,函數根據是否想要被外界調
使用對象
× √
- 構造函數:
想要創建一個對象,會自動調用的函數就是構造函數,如上,構造函數與類同名,沒有返回類型,不需要有,因為構造函數就是要來創建對象的,此處對象就是complex,上面定義的構造函數使用了列表初始化,並且這種初始化只有構造函數才有,其它函數是沒有的,並且給出了default argument
這裏多說一些,一個數值的設定有兩個階段,一個是初始化,一個是後面再賦值(assign),所以列表初始化和在函數體賦值是不同的。
與構造函數對應是析構函數,但是這個例子中不需要寫析構函數,前面提到過class的經典分類,不帶指針的class多半需用寫析構函數
構造函數可以有多個-overloading,但是存在歧義的不可以,如下:
complex() :re(0), im(0) {}//不可以,ambiguous
如下面的對象c2,都沒有給參數,編譯器發現 可以調用上面的,但是編譯器也發現class中的構造函數雖然有參數,但是有默認值,也可以調用,因此編譯器就不知調用哪個了
創建對象
構造函數放在private中,即不允許外界調用構造函數
class A { public: static A& getInstance(); private: A(); A(const A& rhs); }; A& A::getInstance() { static A a; return a; }
外界只能用一份A,即static的部分,外界用的時候不能使用傳統的調用方式,只能通過getInstance()函數
- 參數傳遞與返回值
double real()const { return re; } double imag()const { return im; }
看上面的代碼,都是const型的,因為我不需要改變數據,只是將數據拿出來,看下面兩種情況
A. B.
如果我的函數沒用定義為const類型,B中的調用就是錯誤的,使用者本想,我只是返回數據,並不改變,所以創建了一個常對象,但是由於函數沒有定義為const,所以就會出現調用錯誤。
- 參數傳遞:
pass by value vs pass by reference(to const)
pass by value 就是整包都傳過去,數據多大就傳多大,傳的數據是壓到static棧中的,本例中,傳的是double,是4個字節,傳的就是4個字節,若傳的數據很大呢,效率就低,所以盡量不要pass by value, 可以pass by reference ,reference在底部就是指針,
1 ostream& operator <<(ostream& os, const complex& x) 2 { 3 return os << ‘(‘ << real(x) << ‘,‘ << imag(x) << ‘)‘; 4 } 5 int main() 6 { 7 complex c1(2, 1); 8 complex c2; 9 c2 += c1; 10 cout << c2; 11 }
第7,8行會調用構造函數,通過pass by lalue的方式,第9行會調用1-4行的函數,pass by reference的方式。返回值的傳遞也盡量pass by reference
friend complex& __doapl(complex*, const complex&);//return value :pass by reference
後面會給出上面函數的detail
下面看一種情況,為什麽可以這樣使用
int func(const complex& param) { return param.re + param.im; } //用法 complex c3; c3.func(c1);
可以這樣理解:相同class的各個objects 互為friends
class body外的各種定義什麽情況下可以pass by value,什麽情況下可以pass by reference
首先看看什麽情況不能return by reference
1 inline complex& 2 __doapl(complex* ths, const complex& r) 3 { 4 ths->re += r.re;//第一參數將會被改動 5 ths->im += r.im;//第二參數不會被改動 6 return *ths; 7 } 8 inline complex& 9 complex::operator+=(const complex& r) 10 { 11 return __doapl(this, r); 12 }
這裏,函數將結果放在第一個參數中,有專門的存儲的空間,還有一種情況就是函數專門開辟一個空間存放結果,如果只是
return c1 + c2;
這時,是一個臨時開辟的local變量存儲c1+c2的結果,函數結束,這個臨時變量的生命就結束了,這時,就不能返回reference
- 操作符重載(-1,成員函數) this
想象我們在數學中的復數,有哪些操作,比如相加,復數+復數,復數+實數,復數+純虛數,
繼續看上面代碼,編譯器看到c2+=c1,就會把操作符作用到左邊c2身上,上面的inline complex& complex::operator+=(const complex& r){}相當於
inline complex& complex::operator(this,const complex& r){}
為什麽可以這樣呢,這就是上面辯題中寫道的操作符重載之成員函數,因為所有成員函數帶有一個隱藏的參數this,誰調用這個函數誰就是this,這裏this是c2,
Note:this不可以寫在參數列表中,用可以像函數體中那樣用,return __doapl(this,r);
- return by reference 語法分析
繼續看上面的代碼,看函數定義時的返回類型是complex&,函數體內返回的卻是*ths,這樣是正確的,因為,傳遞者無需知道接收者是以reference形式接收
class body之外的函數定義
operator overloading(操作符重載-2,非成員函數)無this
考慮到用戶的行為,定義了三種+ 形式
1 inline complex 2 operator +(const complex& x, const complex& y) 3 { 4 return complex(real(x) + real(y), imag(x) + imag(y)); 5 } 6 7 inline complex 8 operator +(const complex& x, double y) 9 { 10 return complex(real(x) + y, imag(y)); 11 } 12 13 inline complex 14 operator +(double x, const complex& y) 15 { 16 return complex(x + real(y), imag(y)); 17 }
如上,對於上面的操作符重載,沒有this pointer,因為是全域函數,不再是成員函數
- temp object (臨時對象)tempname()
再看上面的代碼,return by value,對於上面的代碼,絕不可以return by reference,因為他們返回的一定是local object,因為加的結果是函數臨時創建的,函數結束,臨時變量死亡,
解釋一下:tempname(),此處是complex(),就是創建臨時對象,tempname()類似於Int(),
operator +(const complex& x) { return x; } inline complex operator -(const complex& x) { return complex(-real(x), -imag(x)); } //使用 { complex c1(2, 1); complex c2; cout << -c1; cout << +c1; }
這裏是return by value,這裏可是return by reference,因為,這裏結果沒有改變,也沒有創建新的臨時對象,所以是可以return by reference的。
接下來還是操作符重載,考察的東西一樣,語法上沒有新的了,
inline bool operator ==(const complex& x, const complex& y) { return real(x) == real(y) && imag(x) == iamg(y); } inline bool operator ==(const complex& x, double y) { return real(x) == y && iamg(x) == 0; } inline bool operator ==(double x, complex& y) { return x == real(y) && iamg(y) == 0; } inline bool operator !=(const complex& x, complex& y) { return real(x) != real(y) || imag(x) != imag(y); } inline bool operator !=(const complex& x, double y) { return real(x) != y || iamg(x) != 0; } inline bool operator !=(double x, complex& y) { return x != real(y) || iamg(y) != 0; } inline complex conj(const complex& x) { return complex(real(x), -imag(y)); }View Code
ostream& operator <<(ostream& os, const complex& x) { return os << ‘(‘ << real(x) << ‘,‘ << imag(x) << ‘)‘; } //使用 { cout << conj(c1); cout << c1 << conj(c1); }
"<<"作用在左邊,這裏絕不能寫成成員函數,必須寫成全域函數。os也不可是const的,因為每往cout中放入“東西”的時候都是在改變os中的內容,若將返回類型 ostream& ,改為 void 可以嗎?
如果只是輸出一個是可以的(上面第一處使用),如果連續輸出是不可以的(第二處使用),
下面是完整的代碼(還有很多操作沒寫,但寫的已經涵蓋了這種類型的class的幾乎所有語法,有時間再補全):
1 #pragma once 2 #ifndef __COMPLEX__ 3 #define __COMPLEX__ 4 5 class complex { 6 public: 7 complex(double r = 0, double i = 0) :re(r), im(i) {}//construct function 8 //complex() :re(0), im(0) {}//不可以,ambiguous 9 complex& operator +=(const complex&);//two choices,member func or non-member func, 10 //here is member func,do not need to change the value,so is const 11 double real()const { return re; } 12 double imag()const { return im; } 13 int func(const complex& param) 14 { 15 return param.re + param.im; 16 } 17 private: 18 double re, im; 19 friend complex& __doapl(complex*, const complex&);//return value :pass by reference 20 21 }; 22 23 inline complex& 24 __doapl(complex* ths, const complex& r) 25 { 26 ths->re += r.re;//第一參數將會被改動 27 ths->im += r.im;//第二參數不會被改動 28 return *ths; 29 30 } 31 inline complex& 32 complex::operator+=(const complex& r)//the right is not change,so is const,the left 33 {//has already existed,so return by reference 34 return __doapl(this, r); 35 } 36 inline double 37 real(const complex& x) 38 { 39 return x.real(); 40 } 41 inline double 42 imag(const complex& x) 43 { 44 return x.imag(); 45 } 46 //"+"has more than one cases,so set it as the non-member func 47 inline complex 48 operator +(const complex& x, const complex& y) 49 {//the sum result will be put in a local variable,so return by value 50 return complex(real(x) + real(y), imag(x) + imag(y)); 51 } 52 53 inline complex 54 operator +(const complex& x, double y) 55 { 56 return complex(real(x) + y, imag(y)); 57 } 58 59 inline complex 60 operator +(double x, const complex& y) 61 { 62 return complex(x + real(y), imag(y)); 63 } 64 inline complex 65 operator +(const complex& x) 66 { 67 return x; 68 } 69 70 inline complex 71 operator -(const complex& x) 72 { 73 return complex(-real(x), -imag(x)); 74 } 75 76 inline bool 77 operator ==(const complex& x, const complex& y) 78 { 79 return real(x) == real(y) && imag(x) == imag(y); 80 } 81 inline bool 82 operator ==(const complex& x, double y) 83 { 84 return real(x) == y && imag(x) == 0; 85 } 86 87 inline bool 88 operator ==(double x, complex& y) 89 { 90 return x == real(y) && iamg(y) == 0; 91 } 92 93 inline bool 94 operator !=(const complex& x, complex& y) 95 { 96 return real(x) != real(y) || imag(x) != imag(y); 97 } 98 99 inline bool 100 operator !=(const complex& x, double y) 101 { 102 return real(x) != y || imag(x) != 0; 103 } 104 105 inline bool 106 operator !=(double x, complex& y) 107 { 108 return x != real(y) || imag(y) != 0; 109 } 110 111 inline complex 112 conj(const complex& x) 113 { 114 return complex(real(x), -imag(x)); 115 } 116 117 #endifView Code
測試代碼有時間再補
C++ class without pointer