最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解c++良好的編程習(xí)慣與編程要點(diǎn)

 更新時(shí)間:2021年06月09日 10:37:26   作者:lsgxeva  
c++語(yǔ)言的靈活是建立在對(duì)編程者個(gè)人的編程素質(zhì)的嚴(yán)格要求基礎(chǔ)上的,好的C++編程習(xí)慣能避免很多問(wèn)題。沒(méi)有好的編程習(xí)慣,極有可能編寫(xiě)一行代碼,編譯器能報(bào)十幾個(gè)錯(cuò)誤,而且就算編譯通過(guò)了,將來(lái)在運(yùn)行過(guò)程中也會(huì)有很多莫名奇妙的問(wèn)題

1、以良好的方式編寫(xiě)C++ class

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

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

complex.h:

# ifndef  __COMPLEX__
# define __COMPLEX__
class complex
{
    
}
# endif

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

把數(shù)據(jù)放在private聲明下,提供接口訪(fǎng)問(wèn)數(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ì)改變類(lèi)屬性(數(shù)據(jù)成員)的成員函數(shù),全部加上const聲明

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

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

既然函數(shù)不會(huì)改變對(duì)象,那么就如實(shí)說(shuō)明,編譯器能幫你確保函數(shù)的const屬性,閱讀代碼的人也明確你的意圖。
而且,const對(duì)象才可以調(diào)用這些函數(shù)——const對(duì)象不能夠調(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 類(lèi)添加一個(gè)+=操作符:

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

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

函數(shù)返回值也盡量使用引用

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

說(shuō)回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)建的對(duì)象,不能返回引用
    imag(x)+ imag(y));
}

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

c3 += c2 += c1;

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

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

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

提供給外界使用的接口,放在類(lèi)聲明的最前面

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

2、Class with pointer member(s)

記得寫(xiě)B(tài)ig Three

C++的類(lèi)可以分為帶指針數(shù)據(jù)成員與不帶指針數(shù)據(jù)成員兩類(lèi),complex就屬于不帶指針成員的類(lèi)。而這里要說(shuō)的字符串類(lèi)String,一般的實(shí)現(xiàn)會(huì)帶有一個(gè)char *指針。帶指針數(shù)據(jù)成員的類(lèi),需要自己實(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;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 b = a;

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

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

/*拷貝賦值函數(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)在于:

① 處理自我賦值,如果不存在自我賦值問(wèn)題,繼續(xù)下列步驟:

② 釋放自身已經(jīng)申請(qǐng)的內(nèi)存

③ 申請(qǐng)一塊大小與目標(biāo)字符串一樣大的內(nèi)存

④ 進(jìn)行字符串的拷貝

對(duì)于a = b,②③④過(guò)程如下:

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

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í)行了對(duì)象的自我賦值,左右pointers指向同一個(gè)內(nèi)存塊,前面的步驟②delete掉該內(nèi)存塊造成下面的結(jié)果。當(dāng)企圖對(duì)rhs的內(nèi)存進(jìn)行訪(fǎng)問(wèn)是,結(jié)果是未定義的。

3、static與類(lèi)

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

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

static成員函數(shù)沒(méi)有this指針

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

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

而static成員函數(shù)即可以通過(guò)對(duì)象來(lái)調(diào)用,也可以通過(guò)類(lèi)名稱(chēng)來(lái)調(diào)用。

在類(lèi)的外部定義static成員變量

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

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

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

static與類(lèi)的一些小應(yīng)用

這些可以用來(lái)應(yīng)付一下面試,在實(shí)現(xiàn)單例模式的時(shí)候,static成員函數(shù)與static成員變量得到了使用,下面是一種稱(chēng)為”餓漢式“的單例模式的實(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è)置為私有,不允許用戶(hù)代碼創(chuàng)建對(duì)象。要獲取對(duì)象實(shí)例需要通過(guò)接口getInstance?!别I漢式“缺點(diǎn)在于無(wú)論有沒(méi)有代碼需要a,a都被創(chuàng)建出來(lái)。下面是改進(jìn)的單例模式,稱(chēng)為”懶漢式“:

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的寫(xiě)時(shí)復(fù)制,真正需要的時(shí)候才分配內(nèi)存給string對(duì)象管理的字符串。

以上就是詳解c++良好的編程習(xí)慣與編程要點(diǎn)的詳細(xì)內(nèi)容,更多關(guān)于c++良好的編程習(xí)慣與編程要點(diǎn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺析成員函數(shù)和常成員函數(shù)的調(diào)用

    淺析成員函數(shù)和常成員函數(shù)的調(diào)用

    下面小編就為大家?guī)?lái)一篇淺析成員函數(shù)和常成員函數(shù)的調(diào)用。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考,一起跟隨小編過(guò)來(lái)看看吧
    2016-05-05
  • C語(yǔ)言字符串壓縮之ZSTD算法詳解

    C語(yǔ)言字符串壓縮之ZSTD算法詳解

    快速壓縮工具zstd(zstandard)是由facebook開(kāi)源的快速無(wú)損壓縮算法,主要應(yīng)用于zlib級(jí)別的實(shí)時(shí)壓縮場(chǎng)景,并且具有更好的壓縮比。本文將來(lái)講講ZSTD算法的使用,需要的可以參考一下
    2022-08-08
  • C/C++ 編譯器優(yōu)化介紹

    C/C++ 編譯器優(yōu)化介紹

    這篇文章主要涉及了C/C++ 編譯器優(yōu)化的簡(jiǎn)單介紹,具有一定參考價(jià)值。如有不對(duì)之處,歡迎指出。
    2017-09-09
  • 關(guān)于C/C++中的side effect(負(fù)效應(yīng))和sequence point(序列點(diǎn))

    關(guān)于C/C++中的side effect(負(fù)效應(yīng))和sequence point(序列點(diǎn))

    不知你在寫(xiě)code時(shí)是否遇到這樣的問(wèn)題?int i = 3; int x = (++i) + (++i) + (++i); 問(wèn)x值為多少?進(jìn)行各種理論分析,并在編譯器上實(shí)踐,然而可能發(fā)現(xiàn)最終的結(jié)果是不正確的,也是不穩(wěn)定的,不同的編譯器可能會(huì)產(chǎn)生不同的結(jié)果。這讓人很頭疼
    2013-10-10
  • 深入理解Qt 初始項(xiàng)目代碼

    深入理解Qt 初始項(xiàng)目代碼

    本文解析了Qt項(xiàng)目創(chuàng)建后的主要文件,包括main.cpp、mywidget.h、mywidget.cpp、mywidget.ui和.pro,main.cpp這五個(gè)文件,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-02-02
  • C語(yǔ)言轉(zhuǎn)義字符詳解

    C語(yǔ)言轉(zhuǎn)義字符詳解

    這篇文章主要介紹了C語(yǔ)言轉(zhuǎn)義字符詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了C語(yǔ)言轉(zhuǎn)義字符該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C語(yǔ)言實(shí)現(xiàn)三子棋的步驟和代碼詳解

    C語(yǔ)言實(shí)現(xiàn)三子棋的步驟和代碼詳解

    這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)三子棋的步驟和代碼詳解,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • C語(yǔ)言實(shí)現(xiàn)輸出鏈表中倒數(shù)第k個(gè)節(jié)點(diǎn)

    C語(yǔ)言實(shí)現(xiàn)輸出鏈表中倒數(shù)第k個(gè)節(jié)點(diǎn)

    這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)輸出鏈表中倒數(shù)第k個(gè)節(jié)點(diǎn),主要涉及鏈表的遍歷操作,是數(shù)據(jù)結(jié)構(gòu)中鏈表的常見(jiàn)操作。需要的朋友可以參考下
    2014-09-09
  • C++中虛繼承時(shí)的構(gòu)造函數(shù)示例詳解

    C++中虛繼承時(shí)的構(gòu)造函數(shù)示例詳解

    在虛繼承中,虛基類(lèi)是由最終的派生類(lèi)初始化的,換句話(huà)說(shuō),最終派生類(lèi)的構(gòu)造函數(shù)必須要調(diào)用虛基類(lèi)的構(gòu)造函數(shù),這跟普通繼承不同,在普通繼承中,派生類(lèi)構(gòu)造函數(shù)中只能調(diào)用直接基類(lèi)的構(gòu)造函數(shù),不能調(diào)用間接基類(lèi)的,所以本文將通過(guò)代碼示例給大家介紹一下C++虛繼承構(gòu)造函數(shù)
    2023-09-09
  • C++ Boost Optional示例超詳細(xì)講解

    C++ Boost Optional示例超詳細(xì)講解

    Boost是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱(chēng)。Boost庫(kù)是一個(gè)可移植、提供源代碼的C++庫(kù),作為標(biāo)準(zhǔn)庫(kù)的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開(kāi)發(fā)引擎之一,是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱(chēng)
    2022-11-11

最新評(píng)論

瑞丽市| 霞浦县| 淄博市| 乐平市| 安仁县| 邵武市| 汕头市| 海丰县| 文山县| 滦南县| 嘉黎县| 宜兰县| 织金县| 扬中市| 木里| 黄陵县| 高阳县| 枣强县| 论坛| 温泉县| 屏边| 平武县| 黄浦区| 濮阳县| 聂拉木县| 二手房| 栖霞市| 穆棱市| 潮州市| 永胜县| 宁强县| 海原县| 沾化县| 敦煌市| 明光市| 防城港市| 石林| 桦南县| 泉州市| 监利县| 鄄城县|