2011-10-09 48 views
4

我有一個經典的虛擬繼承鑽石:參數化的構造幾乎繼承抽象類

class A { 
protected: 
    A(const char *x) { ... } 
} 

class B: public virtual A { 
protected: 
    B(): A(NULL) { ... } 
public: 
    virtual void foo() = 0; 
} 

class C: public virtual A { 
protected: 
    C(): A(NULL) { ... } 
public: 
    virtual void bar() = 0; 
} 

class D: public B, public C { 
public: 
    D(const char *x): A(x) { ... } 
    void foo(); 
    void bar(); 
} 

B使用NULL和C因爲他們是抽象類的構造函數A將永遠不會在他們的constructurs叫。有沒有更好的方法來實現它,除了在構造函數中指定NULL或在A中聲明無參數構造函數?我想用參數調用構造函數,因此應該只允許在抽象類中使用該構造函數。

+0

我還可以定義一些AbstractPlaceholder,並曾以此爲'A(常量AbstractPlaceholder&AP){斷言(假); }' - 這可能是最好的,但也許有人知道更好的解決方案,它可以讓A不知道它會被虛擬繼承。 –

+0

「,因爲它們是抽象類,所以A ctor將永遠不會在構造器中被調用。」怎麼樣? –

+0

非常有趣。我猜想編譯器沒有理由知道'B'是否可以實例化,所以B的構造函數*有*可以爲它的'A'子對象調用一個構造函數,即使這個子對象是虛擬的,真實的構造函數調用可能會被替換。在道義上,我們想要的是'A'有一個純虛構造器,'虛擬A()= 0;',可以被虛擬的抽象派生類使用。 –

回答

0

我正在關閉此作爲可能不存在的rigth答案。 IMO最好的解決方法是

struct AbstractPlaceholder { 
    AbstractPlaceholder() { 
     assert(false); 
    } 
}; 

class A { 
protected: 
    A(const AbstractPlaceholder &ap) {} 
    A(const char *x) { ... } 
}; 
0

您可以更改A到:

class A { 
private: 
    A() {}; 
    friend class B; 
    friend class C; 
protected: 
    A(const char *x) { } 
}; 

然後B(): A() {}C(): A() {}會的工作,但D(const char*): A() {}不會。但這真的很尷尬。我會堅持你目前使用的NULL

雖然這確實是一個有趣的案例。我可以想到沒有技術原因,你爲什麼要指定A的構造函數BC,因爲它們永遠不會被創建,並且繼承它們的任何人都將初始化A

+0

「他們永遠不會被創造」?!?! –

+0

我不確定該怎麼說......他們是抽象的,所以不會有'新C'。這應該怎麼更好措辭? – eran

0

可以在A(const char*)

class A { 
protected: 
    A(const char *x = 0) { ... } 
        ^^^^ 
}; 

指定默認參數然而,這也將讓D避免A()

+0

是的,的確如此 - 這不是一個解決方案。 –

0

你的方法的變化將是

class A { 
protected: 
    A(const char *x = NULL) { 
     assert(x && "A mustn't be default constructed!"); 
    } 
}; 

從而增加一個更有意義的診斷。


但是,您可能希望以明確允許xNULL(通過C合法建築),那麼你可以使用一個Maybe類型。

template <typename T> class Maybe { 
    T const t; // must be default constructible! 
    bool const invalid; 
    public: 
    Maybe() : t(), invalid(true) {} 
    Maybe(T t) : t(t), invalid(false) {} 
    bool nothing() const { 
     return invalid; 
    } 
    T just() const { 
     assert(!invalid); 
     return t; 
    } 
}; 

那麼您可以在構造改變

A::A(Maybe<const char*> mx) { 
    // either 
    assert(!mx.nothing()); 
    // or 
    mx.just(); 
}