天天看點

(一二八)比較成員函數、中括号表示法、靜态成員函數

有比較函數是strcmp (參數1, 參數2)

參數是兩個字元串,所在頭檔案是<string>

比較方法是按順序依次比較參數1和參數2的第一個字元(看ascii值)。

假如相同,則比較下一個;

假如參數1的比參數2的大,則傳回1(正數);

假如參數1的比參數2的小,則傳回-1(負數);

假如兩個字元串完全一樣,則傳回0。

其原理是(這個我自己寫的):

注意:

①具體實作可能有所差別,但都是逐個判斷;

②strcmp函數裡的指針移動,不影響函數外的指針(因為這裡是按值傳遞)。

可以通過比較函數,編寫類的比較成員函數,或者是友元函數。

作用是,比較一個字元串和一個類對象(事實上是這個類對象的某個同類成員)的關系。

例如,代碼:

①strcmp的兩個參數,是char*類型,是以不能使用string類型進行比較;

②string類型的比較,可以直接使用 >、<、= 比較2個string類字元串的大小(傳回true或者false,效果同這種方法);

③為了友善,有必要加入友元函數進行比較,方法類似,如:

bool operator<(const char*word, player& m)

{

return (m>word);

}

用中括号表示法通路字元:

中括号表示法其實就是“[]”這個符号。

在之前,假如有一個指針指向一個字元串char*a="abcd"; 那麼a[0]=a;

char *a = new char[5];

strcpy_s(a, 5,"abcd");

a[0] = 'b';

cout << a << endl;

而這樣一段代碼中,a[0]可以把第一個字元改為字元'b',

同樣,可以用運算符重載的形式,将這種方法用作類(注意,中括号隻能作為成員函數重載)。

如代碼:

顯示:

①a[0]中,a表示的是調用[]重載運算符的對象,0是參數。(可以參照指針指向字元串來了解)。

②因為傳回的是引用char&,是以,這種方法可以修改私有成員的資料(a[0] = 'c';);

③假如涉及到對象數組,例如man b[3],那麼b[0]表示的則不是對象的第一個字元,而是表示的是b[0]這個對象。如果需要表示第一個字元,則使用b[0][0]。

④具體傳回什麼,可以根據需要自行定義。這裡傳回的是私有成員lname的某個字元(根據參數決定)。

假如這裡面對是被const關鍵字所限定的對象(例如傳回的、或者調用的對象是const所限定的const man a;),那麼則需要修改函數定義,具體需要看情況。

①假如是成員被const所限定,那麼,首先肯定不能直接傳回引用(因為引用涉及修改),可以傳回被const所限定的引用、或者是傳回副本(即比如說傳回char類型)。

如:const string lname="bbb";

則函數定義修改為:

const char& man::operator[](int m)

return lname[m];

這裡的const表示傳回值被const所限定,是以不能成為左值。

②假如是類對象被const所限定,如:const man a;,這裡表示對象的成員不能被修改。那麼:

(1)需要有符合其的重載定義(成員函數在括号後加const表示限定成員不能被修改);

(2)而傳回值不能直接傳回引用(因為這樣可能導緻會被修改),應傳回普通類型或者是被const所限定的引用。

如:

const char& man::operator[](int m)const

第一個const表示傳回值被限定,第二個const表示成員對象被限定。

③是以,如果要為被const限定的對象和不被const限定的對象(後者友善修改,且前提是傳回值的成員沒有被const限定)我們可以同時準備兩個中括号運算符重載的定義,當遇見被const限定的對象時,則比對const版的,否則比對非const版的。

char& man::operator[](int m)

④另外,如果無需修改的話,可以直接使用const版的(放棄無const版),未被const限定的對象,也可以比對const版的函數使用。

靜态成員函數:

靜态成員函數有點類似類的靜态資料成員。

首先,靜态成員函數的函數聲明,必須包含關鍵字static;(但如果函數定義是獨立的——指不屬于任何一個類,而靜态成員函數必須屬于一個類,則不能使用關鍵字static)

其次,靜态成員函數,在類外定義的話,無需再次加入static表示其是靜态函數,隻需要加上作用域運算符即可。例如類man的靜态成員函數static int show();在定義的時候,函數原型是int man::show()這樣

第三,不能通過  對象名.函數名  這樣調用靜态成員函數(因為他不屬于某一個對象,屬于整個類),也是以,無法使用this指針(因為this指針指向的是目前對象)。靜态成員函數的調用方法是: 類名::靜态成員函數名  ; 

最後,靜态成員函數,隻能通路同類裡的靜态成員(因為是他不屬于對象,是以不能通路屬于對象的非靜态成員)。

這也就是為什麼靜态私有成員可以在函數外聲明,而不能在類的非成員函數和非友元函數中通路。是因為靜态成員屬于整個類(而類定義不配置設定記憶體)。

而靜态成員函數,可以用于設定類級(classwide)标記,以控制某些接口的行為。例如,類級标記可以控制顯示類内容的方法所用的格式。

比如說在某種情況下,調用一次靜态成員函數(可以用 類型::靜态成員函數名  這樣的方式),然後把靜态資料成員+1,于是,某些成員函數以靜态資料成員為判斷條件的,則可以改變執行的代碼)。

另外,靜态私有資料成員之是以要在類定義和函數定義之外聲明,兩個原因:

①非成員、友元函數内部不能通路類的私有成員;

②成員函數、友元函數在能使用的時候,已經建立了類對象了,而多個類對象共享一個靜态資料成員,是以需要先有靜态資料成員,後有類對象才可以;(但若全局聲明一個類對象,再初始化靜态資料成員似乎也沒有影響,不過這種對象在靜态記憶體區域,好吧,我也搞不懂)

③不能和類在同一個頭檔案聲明。是因為頭檔案可以多次重複引用,會導緻多重聲明。

而#ifndef  #endif 和#pragma once的作用,是防止在同一個檔案内多次引用頭檔案,但對于多個檔案引用同一個頭檔案,并沒有作用。

指派運算符的再次重載:

假如有構造函數:man(const char* a);

那麼若使用man a = "abc"; 

編譯器則會調用構造函數,建立一個臨時對象("abc"作為參數),然後使用複制構造函數,将臨時對象的指派給對象a,再然後調用析構函數,删除這個臨時對象。

這裡是1,就表示析構函數被調用了,否則無顯示。

當類對象比較小的時候(成員很少),可能沒什麼大的影響。但若類成員比較多(比如有100個),那麼就會影響程式效率(因為會調用構造函數生成臨時對象,還要再調用析構函數删除這個對象)。

是以,可以考慮重載指派運算符,參數為const char*

新的運算符重載函數定義為:

int man::operator=(const char*m)

lname = m;

return 1;

此時,重新運作程式,這次便無任何顯示了。注意,假如是char*指針,則不能直接用lname=m這種形式,而是應通過delete 和new來進行。

另外,之是以需要有傳回值,是因為指派運算符的表達式本身就有值,例如:

cout<< (a=0) <<endl;  便會顯示0,而 cout<< (a=100) <<endl; 則會顯示100。而cout << (a = 'b') << endl;則顯示的為字元b。

也就是說,其輸入什麼,在原本的指派運算符中,其傳回值便是什麼。

而這裡我們自定義的指派運算符重載,傳回值可以由我們自己決定。

以上便是類定義的優化,現我根據書上更改後的類聲明,自行編寫類定義,然後用書上給的程式進行測試。

#ifndef string1_h_

#define string1_h_

#include<iostream>

using std::ostream;

using std::istream;

class string

private:

char * std;

int len;

static int num_strings;

static const int cinlim = 80;

public:

string(const char * s);

string();

string(const string &);

~string();

int length()const {return len;}

string & operator=(const string &);

string & operator=(const char*);

char & operator[](int i);

const char & operator[] (int i) const;

friend bool operator<(const string &st, const string& st2);

friend bool operator>(const string &st1, const string &st2);

friend bool operator==(const string &st, const string &st2);

friend ostream & operator<<(ostream & os, const string &st);

friend istream & operator>>(istream & is, string &st);

statici nt howmany();

};

#endif

以上是類定義,

現将完成程式如下:

測試結果:

總結:

①犯錯的一個地方在于:istream & operator>>(istream & is, string &st)

//輸入運算符重載   這個函數,隻記得delete,但忘記new。是以退出程式時會出錯,補充後ok。

②在①函數中,第一次未添加while (is.get() != '\n')is.get();出錯,表現是未讀取換行符。添加用于讀取換行符後正常。

③沒注意到原代碼是在類成員函數定義檔案中聲明的靜态變量,導緻出錯一次。在1.cpp檔案中補充後正常。

④假如輸入空行提前結束輸入,會導緻依然顯示11 string objects,原因在于測試程式的循環是這樣的,會要求建立10個新對象,再加上string name這個對象,是以共計11個。

⑤另一個錯誤在于,在指派運算符重載時(面向類對象的),忘記加入面對自身時的條件判斷了。這個錯誤會導緻假如把自己指派給自己,可能會出錯的問題。

繼續閱讀