RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

探索C++的編程習(xí)慣與編程要點(diǎn)

dyquk4xk2p3d ? 來源:網(wǎng)絡(luò)整理 ? 2023-11-14 09:25 ? 次閱讀

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

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

① 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

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

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

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

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

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

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

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

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

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

為complex 類添加一個(gè)+=操作符:

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

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

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

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

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

而operator +,其返回值不能是引用,因?yàn)閍+b的值,在調(diào)用operator +的時(shí)候才產(chǎn)生。

下面是operator+=與’operator +’ 的實(shí)現(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;

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

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

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

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

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));
}

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

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

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

C++的類可以分為帶指針數(shù)據(jù)成員與不帶指針數(shù)據(jù)成員兩類,complex就屬于不帶指針成員的類。而這里要說的字符串類String,一般的實(shí)現(xiàn)會(huì)帶有一個(gè)char *指針。帶指針數(shù)據(jù)成員的類,需要自己實(shí)現(xiàn)class三大件:拷貝構(gòu)造函數(shù)、拷貝賦值函數(shù)、析構(gòu)函數(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;
}

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

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

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

/*String的構(gòu)造函數(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 = '';
    }
}

這個(gè)構(gòu)造函數(shù)以C風(fēng)格字符串為參數(shù),當(dāng)執(zhí)行

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

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

9a90ac70-828b-11ee-939d-92fbcf53809c.png

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

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

②賦值構(gòu)造函數(shù)與復(fù)制構(gòu)造函數(shù)負(fù)責(zé)進(jìn)行深拷貝

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

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

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

9aa4fe32-828b-11ee-939d-92fbcf53809c.png

如果此時(shí)執(zhí)行

b = a;

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

9ab57442-828b-11ee-939d-92fbcf53809c.png

存儲World的內(nèi)存塊沒有指針?biāo)赶?,已?jīng)成了一塊無法利用內(nèi)存,從而發(fā)生了內(nèi)存泄露。不止如此,如果此時(shí)對象a被刪除,使用我們上面所寫的析構(gòu)函數(shù),存儲Hello的內(nèi)存塊就被釋放調(diào)用,此時(shí)b.m_data成了一個(gè)野指針。來看看我們自己實(shí)現(xiàn)的構(gòu)造函數(shù)是如何解決這個(gè)問題的,它復(fù)制的是指針?biāo)傅膬?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)典實(shí)現(xiàn),要點(diǎn)在于:

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

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

9ac6899e-828b-11ee-939d-92fbcf53809c.png

9ad686a0-828b-11ee-939d-92fbcf53809c.png

9ae058ce-828b-11ee-939d-92fbcf53809c.png

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

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

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

9ae947ae-828b-11ee-939d-92fbcf53809c.png

static與類

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

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

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

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

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

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

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

另一個(gè)問題是static成員變量的定義。static成員變量必須在類外部進(jìn)行定義:

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

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

④static與類的一些小應(yīng)用

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

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

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

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

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

編輯:黃飛

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8575

    瀏覽量

    151014
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4327

    瀏覽量

    62569
  • 指針
    +關(guān)注

    關(guān)注

    1

    文章

    480

    瀏覽量

    70551
  • C++
    C++
    +關(guān)注

    關(guān)注

    22

    文章

    2108

    瀏覽量

    73618
  • static
    +關(guān)注

    關(guān)注

    0

    文章

    33

    瀏覽量

    10366

原文標(biāo)題:C++ 編程習(xí)慣與編程要點(diǎn)

文章出處:【微信號:良許Linux,微信公眾號:良許Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    嵌入式中C++編程技巧

    假設(shè)現(xiàn)在我們要實(shí)現(xiàn)一個(gè)復(fù)數(shù)類complex,在類的實(shí)現(xiàn)過程中探索良好的編程習(xí)慣。
    發(fā)表于 10-31 11:19 ?795次閱讀
    嵌入式中<b class='flag-5'>C++</b><b class='flag-5'>編程</b>技巧

    Google C++編程指南

    Google C++編程指南目標(biāo):增強(qiáng)代碼一致性,創(chuàng)建通用的、必需的習(xí)慣用語和模式可以使代碼更加容易理解C++是一門包含大量高級特性的巨型語言,某些情況下,我們會(huì)限制甚至禁止使用某些特
    發(fā)表于 11-29 09:15

    Visual C++ 6.0 高級編程 -下載

    Visual C++ 6.0 高級編程,免費(fèi)下載:全面介紹了Visual C++ 6.0的中高級編程技術(shù),其內(nèi)容主要有:內(nèi)存管理、高級圖形處理、使用Internet、創(chuàng)建多線程程序、創(chuàng)
    發(fā)表于 07-12 15:25 ?0次下載
    Visual <b class='flag-5'>C++</b> 6.0 高級<b class='flag-5'>編程</b> -下載

    編程C C++初學(xué)者+FAQ

    編程C C++初學(xué)者+FAQ
    發(fā)表于 09-06 14:55 ?80次下載

    C++編程思想

    C++編程思想,很好的資料,大家下載看看吧!夠20字了吧,哈哈哈!
    發(fā)表于 11-17 11:38 ?0次下載

    Visual C++編程技術(shù)文檔

    Visual C++編程技術(shù)文檔!資料來源網(wǎng)絡(luò),如有侵權(quán),敬請見諒
    發(fā)表于 11-20 15:00 ?0次下載

    高質(zhì)量 C++/C 編程指南

    高質(zhì)量 C++/C 編程指南。
    發(fā)表于 04-05 14:59 ?14次下載

    Android C++高級編程----使用NDK

    Android C++高級編程----使用NDK
    發(fā)表于 03-19 11:23 ?3次下載

    如何進(jìn)行高質(zhì)量的CC++編程?高質(zhì)量C++C編程指南詳細(xì)資料免費(fèi)下載

    本文檔的作用內(nèi)容詳細(xì)介紹的是如何進(jìn)行高質(zhì)量的C、C++編程?高質(zhì)量C++、C編程指南詳細(xì)資料免費(fèi)
    發(fā)表于 09-10 08:00 ?30次下載

    C++編程調(diào)試秘笈

    C++編程調(diào)試秘笈資料下載。
    發(fā)表于 06-01 15:35 ?15次下載

    CC++經(jīng)典著作-C專家編程.PDF

    CC++經(jīng)典著作-C專家編程.PDF
    發(fā)表于 12-13 17:11 ?0次下載

    CC++實(shí)物精選《C專家編程

    CC++實(shí)物精選《C專家編程
    發(fā)表于 01-17 09:55 ?0次下載

    C 語言編程習(xí)慣總結(jié)

    編程習(xí)慣的培養(yǎng)需要的是一個(gè)長期的過程,需要不斷地總結(jié),積累,并且我們需要從意識上認(rèn)識其重要性,一個(gè)良好的編程習(xí)慣對于我們能力的...
    發(fā)表于 01-26 17:15 ?0次下載
    <b class='flag-5'>C</b> 語言<b class='flag-5'>編程</b><b class='flag-5'>習(xí)慣</b>總結(jié)

    C++ 奪冠!2022 年度編程語言

    2022年年度編程語言揭榜啦!在上個(gè)月預(yù)想的C++C、Python三種候選語言中,C++脫穎而出,成為TIOBE2022年度編程語言的最終
    的頭像 發(fā)表于 01-14 09:52 ?1028次閱讀
    <b class='flag-5'>C++</b> 奪冠!2022 年度<b class='flag-5'>編程</b>語言

    c++怎么開始編程

    C++是一種高級的、通用的編程語言,用于開發(fā)各種類型的應(yīng)用程序。它是從C語言演變而來,也是一種靜態(tài)類型語言,可以在不同的平臺上進(jìn)行開發(fā)。C++具有高度的靈活性和性能,并且廣泛應(yīng)用于游戲
    的頭像 發(fā)表于 11-27 15:56 ?924次閱讀
    RM新时代网站-首页