有比較函數是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個。
⑤另一個錯誤在于,在指派運算符重載時(面向類對象的),忘記加入面對自身時的條件判斷了。這個錯誤會導緻假如把自己指派給自己,可能會出錯的問題。