日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > 單片機 > 程序喵大人


以良好的方式編寫C++ class

假設現(xiàn)在我們要實現(xiàn)一個復數(shù)類complex,在類的實現(xiàn)過程中探索良好的編程習慣。

① Header(頭文件)中的防衛(wèi)式聲明

complex.h:

# ifndef  __COMPLEX__ # define __COMPLEX__ class complex {

} # endif

防止頭文件的內(nèi)容被多次包含。

② 把數(shù)據(jù)放在private聲明下,提供接口訪問數(shù)據(jù)

# ifndef  __COMPLEX__ # define __COMPLEX__ class complex { public: double real() const {return re;} double imag() const {return im;} private:
 doubel re,im;
} # endif

③ 不會改變類屬性(數(shù)據(jù)成員)的成員函數(shù),全部加上const聲明

例如上面的成員函數(shù):

double real () `const` {return re;} double imag() `const` {return im;}

既然函數(shù)不會改變對象,那么就如實說明,編譯器能幫你確保函數(shù)的const屬性,閱讀代碼的人也明確你的意圖。

而且,const對象才可以調(diào)用這些函數(shù)——const對象不能夠調(diào)用非const成員函數(shù)。

④ 使用構造函數(shù)初始值列表

class complex { public: complex(double r = 0, double i =0)
 : re(r), im(i)  { } private: doubel re,im;
}

在初始值列表中,才是初始化。在構造函數(shù)體內(nèi)的,叫做賦值。

⑤如果可以,參數(shù)盡量使用reference to const

為complex 類添加一個+=操作符:

class complex { public: complex& operator += (const complex &)
}

使用引用避免類對象構造與析構的開銷,使用const確保參數(shù)不會被改變。內(nèi)置類型的值傳遞與引用傳遞效率沒有多大差別,甚至值傳遞效率會更高。例如,傳遞char類型時,值傳遞只需傳遞一個字節(jié);引用實際上是指針實現(xiàn),需要四個字節(jié)(32位機)的傳遞開銷。但是為了一致,不妨統(tǒng)一使用引用。

⑥ 如果可以,函數(shù)返回值也盡量使用引用

以引用方式返回函數(shù)局部變量會引發(fā)程序未定義行為,離開函數(shù)作用域局部變量被銷毀,引用該變量沒有意義。但是我要說的是,如果可以,函數(shù)應該返回引用。當然,要放回的變量要有一定限制:該變量的在進入函數(shù)前,已經(jīng)被分配了內(nèi)存。以此條件來考量,很容易決定是否要放回引用。而在函數(shù)被調(diào)用時才創(chuàng)建出來的對象,一定不能返回引用。

說回operator +=,其返回值就是引用,原因在于,執(zhí)行a+=b時,a已經(jīng)在內(nèi)存上存在了。

operator + ,其返回值不能是引用,因為a+b的值,在調(diào)用operator +的時候才產(chǎn)生。

下面是operator+= 與’operator +’ 的實現(xiàn):

inline complex & complex :: operator += (const complex & r)
{ this -> re+= r->re; this -> im+= r->im; return * this;
} inline complex operator + (const complex & x , const complex & y)
{ return complex ( real (x)+ real (y), //新創(chuàng)建的對象,不能返回引用 imag(x)+ imag(y));
}

operator +=中返回引用還是必要的,這樣可以使用連續(xù)的操作:

c3 += c2 += c1;

⑦ 如果重載了操作符,就考慮是否需要多個重載

就我們的復數(shù)類來說,+可以有多種使用方式:

complex c1(2,1); complex c2; c2 = c1+ c2; c2 = c1 + 5; c2 = 7 + c1;

為了應付怎么多種加法,+需要有如下三種重載:

inline complex operator+ (const complex & x ,const complex & y)
{ return complex (real(x)+real(y),
 imag(x+imag(y););
} inline complex operator + (const complex & x, double y)
{ return complex (real(x)+y,imag(x)); inline complex operator + (double x,const complex &y)
{ return complex (x+real(y),imag(y));
}

⑧ 提供給外界使用的接口,放在類聲明的最前面

這是某次面試中,面試官大哥告訴我的。想想確實是有道理,類的用戶用起來也舒服,一眼就能看見接口。

Class with pointer member(s):記得寫B(tài)ig Three

C++的類可以分為帶指針數(shù)據(jù)成員與不帶指針數(shù)據(jù)成員兩類,complex就屬于不帶指針成員的類。而這里要說的字符串類String,一般的實現(xiàn)會帶有一個char *指針。帶指針數(shù)據(jù)成員的類,需要自己實現(xiàn)class三大件:拷貝構造函數(shù)、拷貝賦值函數(shù)、析構函數(shù)。

class String {
 public: String (const char * cstr = 0); String (const String & str); String & operator = (const String & str);
 ~String();
 char * get_c_str() const {return m_data};
 private:
 char * m_data;
}

如果沒有寫拷貝構造函數(shù)、賦值構造函數(shù)、析構函數(shù),編譯器默認會給我們寫一套。然而帶指針的類不能依賴編譯器的默認實現(xiàn)——這涉及到資源的釋放、深拷貝與淺拷貝的問題。在實現(xiàn)String類的過程中我們來闡述這些問題。

①析構函數(shù)釋放動態(tài)分配的內(nèi)存資源

如果class里有指針,多半是需要進行內(nèi)存動態(tài)分配(例如String),析構函數(shù)必須負責在對象生命結束時釋放掉動態(tài)申請來的內(nèi)存,否則就造成了內(nèi)存泄露。局部對象在離開函數(shù)作用域時,對象析構函數(shù)被自動調(diào)用,而使用new動態(tài)分配的對象,也需要顯式的使用delete來刪除對象。而delete實際上會調(diào)用對象的析構函數(shù),我們必須在析構函數(shù)中完成釋放指針m_data所申請的內(nèi)存。下面是一個構造函數(shù),體現(xiàn)了m_data的動態(tài)內(nèi)存申請:

/*String的構造函數(shù)*/ inline String ::String (const char *cstr = 0)
{ if(cstr)
 {
 m_data = new char[strlen(cstr)+1]; // 這里,m_data申請了內(nèi)存 strcpy(m_data,cstr);
 } else {
 m_data= new char[1];
 *m_data = '\0';
 }
}

這個構造函數(shù)以C風格字符串為參數(shù),當執(zhí)行

String *p = new String ("hello");

m_data向系統(tǒng)申請了一塊內(nèi)存存放字符串hello

析構函數(shù)必須負責把這段動態(tài)申請來的內(nèi)存釋放掉:

inline String ::~String()
{ delete[]m_data;
}

②賦值構造函數(shù)與復制構造函數(shù)負責進行深拷貝

來看看如果使用編譯器為String默認生成的拷貝構造函數(shù)與賦值操作符會發(fā)生什么事情。默認的復制構造函數(shù)或賦值操作符所做的事情是對類的內(nèi)存進行按位的拷貝,也稱為淺拷貝,它們只是把對象內(nèi)存上的每一個bit復制到另一個對象上去,在String中就只是復制了指針,而不復制指針所指內(nèi)容。現(xiàn)在有兩個String對象:

String a("Hello");
String b("World");

a、b在內(nèi)存上如圖所示:

如果此時執(zhí)行

b = a;

淺拷貝體現(xiàn)為:

存儲World\0的內(nèi)存塊沒有指針所指向,已經(jīng)成了一塊無法利用內(nèi)存,從而發(fā)生了內(nèi)存泄露。不止如此,如果此時對象a被刪除,使用我們上面所寫的析構函數(shù),存儲Hello\0的內(nèi)存塊就被釋放調(diào)用,此時b.m_data成了一個野指針。來看看我們自己實現(xiàn)的構造函數(shù)是如何解決這個問題的,它復制的是指針所指的內(nèi)存內(nèi)容,這稱為深拷貝

/*拷貝賦值函數(shù)*/ inline String &String ::operator= (const String & str)
{ if(this == &str) //① return *this; delete[] m_data; //② m_data = new char[strlen(str.m_data)+1]; //③ strcpy(m_data,str.m_data); //④ return *this }

這是拷貝賦值函數(shù)的經(jīng)典實現(xiàn),要點在于:

① 處理自我賦值,如果不存在自我賦值問題,繼續(xù)下列步驟:
② 釋放自身已經(jīng)申請的內(nèi)存
③ 申請一塊大小與目標字符串一樣大的內(nèi)存
④ 進行字符串的拷貝

對于a = b,②③④過程如下:

同樣的,復制構造函數(shù)也是一個深拷貝的過程:

inline String ::String(const String & str )
{
 m_data = new char[ strlen (str) +1]; strcpy(m_data,str.m_data);
}

另外,一定要在operator = 中檢查是否self assignment 假設這時候確實執(zhí)行了對象的自我賦值,左右pointers指向同一個內(nèi)存塊,前面的步驟②delete掉該內(nèi)存塊造成下面的結果。當企圖對rhs的內(nèi)存進行訪問是,結果是未定義的。

static與類

① 不和對象直接相關的數(shù)據(jù),聲明為static

想象有一個銀行賬戶的類,每個人都可以開銀行賬戶。存在銀行利率這個成員變量,它不應該屬于對象,而應該屬于銀行這個類,由所有的用戶來共享。static修飾成員變量時,該成員變量放在程序的全局區(qū)中,整個程序運行過程中只有該成員變量的一份副本。而普通的成員變量存在每個對象的內(nèi)存中,若把銀行利率放在每個對象中,是浪費了內(nèi)存。

② static成員函數(shù)沒有this指針

static成員函數(shù)與普通函數(shù)一樣,都是只有一份函數(shù)的副本,存儲在進程的代碼段上。不一樣的是,static成員函數(shù)沒有this指針,所以它不能夠調(diào)用普通的成員變量,只能調(diào)用static成員變量。普通成員函數(shù)的調(diào)用需要通過對象來調(diào)用,編譯器會把對象取地址,作為this指針的實參傳遞給成員函數(shù):

obj.func() ---> Class :: fun(&obj);

而static成員函數(shù)即可以通過對象來調(diào)用,也可以通過類名稱來調(diào)用。

③在類的外部定義static成員變量

另一個問題是static成員變量的定義。static成員變量必須在類外部進行定義:

class A { private: static int a; //① } int A::a = 10; //②

注意①是聲明,②才是定義,定義為變量分配了內(nèi)存。

④static與類的一些小應用

這些可以用來應付一下面試,在實現(xiàn)單例模式的時候,static成員函數(shù)與static成員變量得到了使用,下面是一種稱為”餓漢式“的單例模式的實現(xiàn):

class A { public: static A& getInstance();
 setup(){...}; private:
 A();
 A(const A & rhs); static A a;
}

這里把class A的構造函數(shù)都設置為私有,不允許用戶代碼創(chuàng)建對象。要獲取對象實例需要通過接口getInstance?!别I漢式“缺點在于無論有沒有代碼需要aa都被創(chuàng)建出來。下面是改進的單例模式,稱為”懶漢式“:

class A { public: static A& getInstance();
 setup(){....}; private:
 A();
 A(const A& rsh);
 ...
};
A& A::getInstance()
{ static A a; return a;
}

“懶漢式”只有在真正需要a時,調(diào)用getInstance才創(chuàng)建出唯一實例。這可以看成一個具有拖延癥的單例模式,不到最后關頭不干活。很多設計都體現(xiàn)了這種拖延的思想,比如string的寫時復制,真正需要的時候才分配內(nèi)存給string對象管理的字符串。

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權益,請及時聯(lián)系本站刪除。
關閉