1. 程式人生 > >More Effective C++:Item 27

More Effective C++:Item 27

溫習More Effective C++,對於Item 27:要求或禁止在堆中產生物件,整理思路於此文。

編譯期控制

通過禁用定義物件所需的條件,即可在編譯期阻止物件的定義。

  • 定義堆物件所需的條件
class operator new 建構函式 解構函式
 獨立物件  非必須 不需要,可定義偽建構函式 不需要,可定義偽解構函式
成員直接物件 不需要,整體類定義即可 不需要,可定義偽建構函式 不需要,可定義偽解構函式
成員指標物件 不需要,整體類定義即可 不需要,可定義偽建構函式 不需要,可定義偽解構函式
 基類物件  不需要,派生類定義即可 public / protected public / protected
  • 定義非堆物件所需的條件

|||||
|獨立非堆物件|不需要|public|public|
|成員非堆物件|不需要|public|public|
|基類非堆物件|不需要|public|public|


注意其中一點,作為成員堆物件,其可以是直接物件也可以是指標物件。而作為成員非堆物件,則只能是直接物件。

通過上表不難得出以下結論:

要求物件在堆中

禁用public構造或解構函式即可使得獨立物件和成員物件無法定義在非堆位置,而相應提供偽構造或解構函式就不會影響其在堆中的定義。

然而對於基類物件卻沒有什麼好的辦法,因為無論是否在堆中,其定義條件都是相同的——public或protected的構造和解構函式。若宣告構造或解構函式為private,基類物件的定義就會被完全禁止,也就是說該類將無法作為基類。

// 數字類。
class Number
{
public:
    // 提供偽建構函式。
    static Number* MakeInstance() { return new Number; } 
    virtual ~Number() {}
protected:
    // 將建構函式宣告為protectedNumber() {} 
};

//==============================================================================
// 獨立物件。 //============================================================================== void DefineIndependentObject() { //========================================================================== // 定義在堆中:正確。 //========================================================================== Number* heapObject = Number::MakeInstance(); delete heapObject; //========================================================================== // 定義在棧中:錯誤。 //========================================================================== Number nonheapObject; } //============================================================================== // 成員物件。 //============================================================================== void DefineMemberObject() { //========================================================================== // 定義在堆中:正確。 //========================================================================== { // 財產類包含一個數字類指標。 class Asset { public: Asset() : value( Number::MakeInstance() ) {} ~Asset() { delete value; } private: Number* value; }; Asset* heapObject = new Asset; delete heapObject; } //========================================================================== // 定義在棧中:錯誤。 //========================================================================== { // 財產類包含一個數字類。 class Asset { private: Number value; }; Asset nonheapObject; } } //============================================================================== // 子類物件 //============================================================================== void DefineSubclassObject() { // 負數類派生自數字類。 class NegativeNumber : public Number {}; //========================================================================== // 定義在堆中:正確。 //========================================================================== NegativeNumber* heapObject = new NegativeNumber; delete heapObject; //========================================================================== // 定義在棧中:正確。 //========================================================================== NegativeNumber nonheapObject; }

禁止物件在堆中

只需禁用public class new即可禁止獨立物件定義在堆中,並且不會對其在非堆位置中的定義產生影響。

然而對於成員物件和基類物件卻又不存在什麼好的辦法,因為只有定義了public構造和解構函式,它們才能被定義在非堆位置,然而這也會使得它們能被定義在堆中。同樣的,若是將構造或解構函式定義為private,那麼它們的定義將會被完全禁止。

#include <new>

class Number
{
private:
    static void* operator new( std::size_t ) throw() { return nullptr; }
};

//==============================================================================
// 獨立物件。
//==============================================================================

void DefineIndependentObject()
{
    //==========================================================================
    // 定義在堆中:錯誤。
    //==========================================================================

    Number* heapObject = new Number;
    delete heapObject;

    //==========================================================================
    // 定義在棧中:正確。
    //==========================================================================

    Number nonheapObject;
}

執行期控制

通過堆物件和非堆物件的不同建立流程來進行控制。其不同之處只有一點:建立堆物件時class operator new會被呼叫。然而,首先,這一點只對獨立物件管用:基類堆物件和成員堆物件的class operator new不一定被呼叫。

#include <new>
#include <iostream>

class Number
{
public:
    static void* operator new( std::size_t ) throw() 
    {
        std::cout << "class operator new for Number" << std::endl;
    }
};

class NegativeNumber : public Number
{
public:
    static void* operator new( std::size_t ) throw() 
    {
        std::cout << "class operator new for NegativeNumber" << std::endl;
    }
};

class Asset { Number value; };

int main()
{
    // 獨立堆物件的class operator new被呼叫。
    Number* independentObject = new Number;
    delete independentObject;

    // 看,基類堆物件的則沒有被呼叫。
    NegativeNumber* baseObject = new NegativeNumber;
    delete baseObject;

    // 看,子類堆物件的也沒有被呼叫。
    Asset* memberObject = new Asset;
    delete memberObject;

    return 0;
}

然後,即使是對於獨立物件,它也不怎麼好用,具體請參考More Effective C++ Item27。

總結

控制物件的記憶體位置比較困難,能夠完美實現的只有:
1. 要求獨立物件和成員物件在堆中。
2. 禁止獨立物件在物件。

相關推薦

More Effective C++Item 27

溫習More Effective C++,對於Item 27:要求或禁止在堆中產生物件,整理思路於此文。 編譯期控制 通過禁用定義物件所需的條件,即可在編譯期阻止物件的定義。 定義堆物件所需的條件 class operator n

More Effective C++不使用多型性陣列

類繼承的最重要的特性是你可以通過基類指標或引用來操作派生類。這樣的指標或引用具有行為的多型性,就好像它們同時具有多種形態。C++允許你通過基類指標和引用來操作派生類陣列。不過這根本就不是一個特性,因為這樣的程式碼根本無法如你所願地那樣執行。   假設你有一個類BST(比如是搜

More Effective C++ Item M31讓函式根據一個以上的物件來決定怎麼虛擬

1.3 Item M31:讓函式根據一個以上的物件來決定怎麼虛擬有時,借用一下Jacqueline Susann的話:一次是不夠的。例如你有著一個光輝形象、崇高聲望、豐厚薪水的程式設計師工作,在Redmond,Wshington的一個著名軟體公司--當然,我說的就是任天堂。為

More Effective C++ - 章節一 基礎議題

def pre 基礎 poi 地址 否則 相同 不用 完美世界 1. 仔細區分 pointers 和 references references和pointers的差別描述如下: pointer:當需要考慮"不指向任何對象"時,或者是考慮"在

讀《More Effective C++35個改善程式設計與設計的有效方法》之條款3絕對不要以多型方式處理陣列

有以下程式: <pre name="code" class="cpp">class Base { public: Base(int n = 0) : _b(n) {} int _b; }; class Devide : public Base { publ

More Effective C++: 05技術(25-28)

print div 子類 text 不可移植 double 默認 一次 theme 25:將constructor 和 non-member functions 虛化 所謂 virtual constructor是某種函數,視其輸入可產生不同類型的對象。比

More Effective C++

繼承 高程 malloc 多繼承 放棄 沒有 具體實現 協助 cti More Effective C++讀書筆記 條款1:指針與引用的區別二者之間的區別是:在任何情況下都不能用指向空值的引用,而指針則可以;指針可以被重新賦值以指向另一個不同的對象,但是引用則總是指向在

More Effective C++ 》讀書筆記(二)Exception 異常

derived 對象 模板 帶來 成員 臨時對象 行為 ron 阻止 這事篇讀書筆記,只記錄自己的理解和總結,一般情況不對其舉例子具體說明,因為那正是書本身做的事情,我的筆記作為梳理和復習之用,劃重點。我推薦學C++的人都好好讀一遍Effective C++ 系列,真是好

More Effective C++ 35個做法

前言 最近在看《More Effective C++》這個書,自己 C++ 基礎還是不行,有的地方看的有點懵,最後還是堅持看完了,做做筆記,簡短的 記錄一下有哪些改善程式設計與設計的有效方法。 推薦還是可以買一本原書的,書中例子比較豐富,更容易理解一些。 一、基礎議題 1

More Effective C++》讀書筆記(零)Basic 基礎條款

這是篇讀書筆記,只記錄自己的理解和總結,一般情況不對其舉例子具體說明,因為那正是書本身做的事情,我的筆記作為梳理和複習之用,劃重點。我推薦學C++的人都好好讀一遍Effective C++ 系列,真是好書啊,對於學完C++ 基礎知識的人,這是本高階祕籍。值得注意的是 More Effective C++

More effective C++ 條款25 將建構函式和非成員函式虛擬化

7.1 Item M25:將建構函式和非成員函式虛擬化 從字面來看,談論“虛擬建構函式”沒有意義。當你有一個指標或引用,但是不知道其指向物件的真實型別是什麼時,你可以呼叫虛擬函式來完成特定型別(type-specific)物件的行為。僅當你還沒擁有一個物件但是你又確切地知道想要的物件的型別時,你

2,More Effective C++——條款5(謹慎使用定製“型別轉換函式”)

1 隱式型別轉換 C++中允許如下3種形式的隱式型別轉換: 1. 基本型別隱式型別轉換: int a = 10; double b = a; 2. 單引數構造建構函式 class Name { // 可以將char* 型別轉換成Name型別 Name(con

3 More Effective C++—條款6(自定義自增/自減操作符)

1 過載操作符 C++允許使用者自定義自增、自減操作符。兩種操作符都有對應的前置、後置形式。如下所示: index++ // 返回原值,並自增 ++index // 自增,並返回新值 index-- --index 過載操作符如下程式碼所示。由於前置(pref

6 More Effective C++—條款9(區域性變數的destructor防止記憶體洩漏)

0 生活雞湯 偶然看到一篇文章,每天前進一點點,積累下來,人生就能有所改變。已經有一段時間沒有更新這個系列,今天爭取再往前走一點點。 1 提出問題 寵物醫院提供收養服務,其中,主要收養物件是小狗(Dog)小貓(Cat)。收養需要走一定流程,具體流程我們不必關心。

7 More Effective C++—條款10(建構函式內阻止記憶體洩漏)

1 提出問題 上一篇文章中,我們討論瞭如下情況,當函式doSomething()被呼叫時,heap中資源無法被釋放,導致記憶體洩漏問題發生。 void function() { MyObject *object = new MyObject; object-

8 More Effective C++—條款11(解構函式內阻止異常流出)

1 提出問題 1 解構函式呼叫時機 解構函式會在下面兩種情況下被呼叫: 1, 離開物件所在作用域,物件生命週期終結,解構函式被呼叫,物件被銷燬。 2, 異常丟擲引起了棧展開(stack-unwinding),離開物件的所在的作用域,物件生命週期中介,解構函式被

More effective C++( 4-)

                     

11 More Effective C++—條款14(有效使用異常限定符)

1 異常限定符與unexpected呼叫 如下面的程式碼所示,識別符號throw()即為異常限定符。異常限定符標識了函式可以丟擲的異常型別。當throw後面的括號內容為空,表示該函式不丟擲任何異常。 class Exception { public: co