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:
// 將建構函式宣告為protected。
Number() {}
};
//==============================================================================
// 獨立物件。
//==============================================================================
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),離開物件的所在的作用域,物件生命週期中介,解構函式被
11 More Effective C++—條款14(有效使用異常限定符)
1 異常限定符與unexpected呼叫 如下面的程式碼所示,識別符號throw()即為異常限定符。異常限定符標識了函式可以丟擲的異常型別。當throw後面的括號內容為空,表示該函式不丟擲任何異常。 class Exception { public: co