2012-07-04 95 views
2

假設我有一個類Baz,該類依次從類FooBar繼承。類Bar的構造函數會獲取指向Foo對象的指針。我想這樣做是通過this作爲Foo對象到Bar構造:在初始值設定項列表中使用

Baz() : Foo(), Bar(this) {} 

工作的示例:

#include <iostream> 
class Bar; 
class Foo { 
    public: 
    virtual ~Foo() {} 
    virtual void parse_bar (Bar&) const = 0; 
}; 

class Bar { 
    private: 
    const Foo * parser; 
    public: 
    Bar (const Foo * parser_in) : parser(parser_in) {} 
    virtual ~Bar() {} 
    void parse_self() { parser->parse_bar (*this); } 
}; 

class Baz : public Foo, public Bar { 
    public: 
    Baz() : Foo(), Bar(this) {} 
    virtual void parse_bar (Bar &) const { std::cout << "Hello World\n"; } 
}; 

int main() { 
    Baz baz; 
    baz.parse_self(); 
} 

這發生在我的電腦上工作,用我的編譯器(測試與他們幾個)。然而,2003年標準的第9.3.2節讓我有點不安,因爲我可能只是幸運地用這種方式使用this是未定義的行爲。嚴格來說,初始化器列表在構造函數的主體之外。這裏的相關的文字,重點煤礦:

9.3.2 this指針
體的非靜態成員函數的,關鍵字this是非左值表達式,其值是對象的地址爲此函數被調用。

那麼,我的用法是否合法且定義明確,還是未定義的行爲?

回答

4

在這種情況下有兩點需要注意。

首先,在構造函數初始化列表中this指針指向一個非構造的(或未完全構造的)對象。訪問這樣的指針是可以的,但它所引用的對象只能以有限的方式使用。請參見語言規範中的12.7。

其次,在您的具體示例中,您實際做的是在嘗試訪問之前將this指針轉換爲Foo *類型。這是完全安全的,因爲那個時候Foo子對象被完全構建。 (我認爲,無論如何訪問,如果有的話,將僅限於完全構造的Foo子對象)。

唯一值得關注的是這種情況是否合法將this轉換爲Foo *類型,即轉換過程本身是否應該成功。答案是:是的,在普通(非虛擬)繼承的情況下,這種轉換是完全合法和安全的(再次,在12.7中明確允許)

+0

比我的答案要清楚得多,我會稍微刪除一下。 – GManNickG

+0

@GManNickG Dont!評論可能對新手有用 – TeaOverflow

+0

@Evgeni:對不起。 :)新手!有關類繼承的問題? - > [圖書清單](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list)。 – GManNickG

3

這很好。 C++標準實際上闡明在初始化列表使用this指針:

12.6.2初始化鹼和成員[class.base.init]

第7段:名稱中的expression-list在指定mem-initializer的構造函數的範圍內對mem-initializer進行評估。 [實施例:

class X { 
     int a; 
     int b; 
     int i; 
     int j; 
    public: 
     const int& r; 
     X(int i): r(a), b(i), i(i), j(this->i) {} 
    }; 

初始化X::rX::a,用構造參數i的 值初始化X::b,用構造參數i的 值初始化X::i,和與 的初始化X::j值爲X::i;每次創建類X 的對象時都會發生這種情況。 ] [注:由於mem-initializer在 構造的範圍進行評價,所述this指針可以以mem-initializer的 表達式列表用於指對象是 初始化。 ]

類型中的Baz初始化的this指針的是Baz類型的事實。當然,您必須注意並非所有成員都可能已經初始化。許多(如果不是全部的話)編譯器設置在最高的警告級別(你真的應該這樣做),它會警告你通過this指針指向基類。

但是,看起來你正在使它變得更加複雜。爲什麼不把虛擬功能parse_bar()放在Bar類中,而忘記Foo類?

#include <iostream> 

class Bar 
{    
public:  
    Bar() {}  
    virtual ~Bar() {}  
    void parse_self() { parse_bar(); } 
private: // Template method pattern 
    virtual void parse_bar() const = 0;   
};   

class Baz : public Bar 
{  
public:  
    Baz() {}  
private: // Yes, this does override the private `parse_bar()` member! 
    virtual void parse_bar() const { std::cout << "Hello World\n"; }  
}; 

int main() 
{ 
    Baz baz; 
    baz.parse_self(); 
} 

這基本上是相同的功能,但代碼少。

+0

+1,很好的答案,在硅片上。選擇哪個答案很困難。關於我發佈的代碼和您的建議修正:我的代碼是用來說明使用情況的;一個MWE。它看起來不像我必須處理的代碼。 –

相關問題