那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

探索C++的編程習慣與編程要點

dyquk4xk2p3d ? 來源:網絡整理 ? 2023-11-14 09:25 ? 次閱讀

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

假設現在我們要實現一個復數類complex,在類的實現過程中探索良好的編程習慣。

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

complex.h:

# ifndef  __COMPLEX__
# define __COMPLEX__
class complex
{

}
# endif

防止頭文件的內容被多次包含。

② 把數據放在private聲明下,提供接口訪問數據

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

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

例如上面的成員函數:

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

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

而且,const對象才可以調用這些函數——const對象不能夠調用非const成員函數。

④ 使用構造函數初始值列表

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

在初始值列表中,才是初始化。在構造函數體內的,叫做賦值。

⑤如果可以,參數盡量使用reference to const

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

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

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

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

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

說回operator +=,其返回值就是引用,原因在于,執行a+=b時,a已經在內存上存在了。

而operator +,其返回值不能是引用,因為a+b的值,在調用operator +的時候才產生。

下面是operator+=與’operator +’ 的實現:

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),                        //新創建的對象,不能返回引用
                                 imag(x)+ imag(y));
}

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

c3 += c2 += c1;

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

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

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):記得寫Big Three

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

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

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

①析構函數釋放動態分配的內存資源

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

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

這個構造函數以C風格字符串為參數,當執行

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

m_data向系統申請了一塊內存存放字符串hello:

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

析構函數必須負責把這段動態申請來的內存釋放掉:

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

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

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

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

a、b在內存上如圖所示:

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

如果此時執行

b = a;

淺拷貝體現為:

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

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

/*拷貝賦值函數*/
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
}

這是拷貝賦值函數的經典實現,要點在于:

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

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

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

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

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

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

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

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

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

static與類

① 不和對象直接相關的數據,聲明為static

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

② static成員函數沒有this指針

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

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

而static成員函數即可以通過對象來調用,也可以通過類名稱來調用。

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

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

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

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

④static與類的一些小應用

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

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

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

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

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

編輯:黃飛

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 接口
    +關注

    關注

    33

    文章

    8691

    瀏覽量

    151920
  • 函數
    +關注

    關注

    3

    文章

    4346

    瀏覽量

    62973
  • 指針
    +關注

    關注

    1

    文章

    481

    瀏覽量

    70610
  • C++
    C++
    +關注

    關注

    22

    文章

    2114

    瀏覽量

    73857
  • static
    +關注

    關注

    0

    文章

    33

    瀏覽量

    10407

原文標題:C++ 編程習慣與編程要點

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

收藏 人收藏

    評論

    相關推薦

    嵌入式中C++編程技巧

    假設現在我們要實現一個復數類complex,在類的實現過程中探索良好的編程習慣
    發表于 10-31 11:19 ?869次閱讀
    嵌入式中<b class='flag-5'>C++</b><b class='flag-5'>編程</b>技巧

    Google C++編程指南

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

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

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

    編程C C++初學者+FAQ

    編程C C++初學者+FAQ
    發表于 09-06 14:55 ?80次下載

    C++編程思想

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

    Visual C++編程技術文檔

    Visual C++編程技術文檔!資料來源網絡,如有侵權,敬請見諒
    發表于 11-20 15:00 ?0次下載

    高質量 C++/C 編程指南

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

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

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

    如何進行高質量的CC++編程?高質量C++C編程指南詳細資料免費下載

    本文檔的作用內容詳細介紹的是如何進行高質量的CC++編程?高質量C++C編程指南詳細資料免費
    發表于 09-10 08:00 ?30次下載

    C++編程調試秘笈

    C++編程調試秘笈資料下載。
    發表于 06-01 15:35 ?15次下載

    CC++經典著作-C專家編程.PDF

    CC++經典著作-C專家編程.PDF
    發表于 12-13 17:11 ?0次下載

    CC++實物精選《C專家編程

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

    C 語言編程習慣總結

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

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

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

    c++怎么開始編程

    C++是一種高級的、通用的編程語言,用于開發各種類型的應用程序。它是從C語言演變而來,也是一種靜態類型語言,可以在不同的平臺上進行開發。C++具有高度的靈活性和性能,并且廣泛應用于游戲
    的頭像 發表于 11-27 15:56 ?988次閱讀
    泉州市| 澳门顶级赌场百家乐| 云顶国际网| 至尊百家乐官网节目单| 迪士尼百家乐官网的玩法技巧和规则 | 大发888官网 888| 澳门百家乐官网海洋阿强| 大发8888娱乐场| 百家乐官网家居 | 百家乐官网如何制| 五星百家乐官网的玩法技巧和规则 | 玩百家乐官网游戏的最高技巧| 新世纪百家乐娱乐城| tt线上娱乐城| 百家乐官网b28博你发v| 郑州太阳城宾馆| 半圆百家乐官网桌子| 澳门百家乐规律星期娱乐城博彩| 澳门网上娱乐| 百家乐洗码| 速博国际娱乐| 院子围墙砌18还是24| 顶级赌场怎么样| 百家乐官网赢钱公式论| 德州扑克入门| 百家乐官网桌布动物| 香港六合彩开奖| 百家乐视频地主| 百家乐园百乐彩| 百家乐开户平台| 呼和浩特市| 百家乐赌场导航| 百家乐官网开户优惠多的平台是哪家 | 百家乐官网博娱乐平台| 百家乐手论坛48491| 百家乐官网开发软件| 大发888娱乐城官网| 找真人百家乐官网的玩法技巧和规则 | bet365注册找谁| 百家乐获胜秘决| 百家乐官网保单详图|