2011-09-08 89 views
0

C++中的多態性遇到一些麻煩。我試圖創建一個相當奇怪的語法來初始化一個類,但是當我從基類方法返回「this」時,它似乎失去了新創建的派生類。從C++中的基類返回派生的「this」的多態性

在下面的僞代碼中,我需要讓Base :: initWithPuppies()能夠執行一些默認操作並返回它所調用的派生類。

我想有這樣的語法:

Bar *baz = (new Bar)->initWithPuppies(); 

最好使用模板和需要投就像沒有

initWithPuppies<Bar *>(); 

是的,我知道這是相當怪人。但有一個原因,「最佳實踐」不適用於這種情況。考慮這只是一個「如果」。我知道你應該可能:

Bar *baz = new Bar; 
baz->initWithPuppies(); 

但我需要以前的語法。

僞代碼:

class Base 
{ 
    // Kittens 
}; 

class Foo : public Base 
{ 
    public: 
    Base * initWithPuppies(); 
    virtual void test() = 0; 
}; 

Base * Foo::initWithPuppies() 
{ 
    // Call to derived works 
    this->test(); 

    return this; 
} 

class Bar : public Foo 
{ 
    public: 
    void test(); 
}; 

void Bar::test() 
{ 
    std::cout << "It Works!" << std::endl; 
} 

// Preferred syntax 
// This gives "cannot convert from 'Base *' to 'Bar *' " 
Bar *baz = (new Bar)->initWithPuppies(); 

baz->test(); 

/*------------------------------------------*/ 

// This gives " 'test' : is not a member of 'Base' " 
Base *baz = (new Bar)->initWithPuppies(); 

baz->test(); 

/*------------------------------------------*/ 

// This gives "Base is not a polymorphic type" 
UIBar *man = dynamic_cast<UIBar *>((new UIBar)->initWithFrame()); 

baz->test(); 

編輯:

如果它在某種程度上可能有這樣的語法:

Bar *baz = (Bar::create())->initWithPuppies(); 

這甚至會更好,但我不能弄清楚如何在基類中創建一個不帶類型轉換的派生的新實例:

Bar *baz = (Bar::create<Bar *>())->initWithPuppies(); 

我的回答:(不能回答我自己的8小時)

雖然尼科爾流星錘是正確的,我說我要使用的語法是不好的做法,如果你真的需要使用類似的語法,因爲我(不要問...),那麼你可以這樣做:

class Base 
{ 
    // Kittens 
}; 

class Foo : public Base 
{ 
    public: 
    virtual void test() = 0; 
    private: 
    void _initWithPuppies(); 
}; 

void Foo::initWithPuppies() 
{ 
    // Do shit 
} 

class Bar : public Foo 
{ 
    public: 
    Bar * initWithPuppies(); 
    void test(); 
}; 

Bar * Bar::initWithPuppies() 
{ 
    this->_initWithPuppies(); 

    return this;  
} 

void Bar::test() 
{ 
    std::cout << "It Works!" << std::endl; 
} 


Bar *baz = (new Bar)->initWithPuppies(); 

baz->test(); 
+1

出於好奇,爲什麼函數返回一個'基地*'當你知道它的實際返回'富*'? – templatetypedef

+0

添加虛擬析構函數,首先應刪除一個案例。 –

+0

難道你不能只是超載Bar * Bar :: initWithPuppies調用Foo :: initWithPuppies裏面並返回這個? –

回答

3

東西你可能會發現有用的是C++的爲covariant return types支持。這意味着如果在基類中定義了返回Base *類型對象的函數,則可以覆蓋該方法,使其返回Base*或指向派生類Base的任何指針。例如,此代碼是完全合法的:

class Base { 
public: 
    virtual ~Base() {} // Polymorphic classes need virtual destructors! 
    virtual Base* initWithPuppies() = 0; 
} 

class Derived: public Base { 
public: 
    /* Note that the return type is Derived*, but it's still an override! */ 
    virtual Derived* initWithPuppies() { 
     return this; 
    } 
} 

/* Perfectly legal code; Derived::initWithPuppies() returns a Derived* */ 
Derived* d = (new Derived)->initWithPuppies(); 

希望這有助於您!

+0

由於'initWithPuppies'是在問題非虛,我們只能通過將initWithPuppies過載/隱藏它雖然它讓我有我需要的語法返回任何東西,我們做得特別好,請;-) –

+0

,它這樣做派生類,在我的情況下這是一個冗餘的nono。雖然謝謝! – Jay

9

我想有這樣的語法:

Bar *baz = (new Bar)->initWithPuppies(); 

OK,停在那兒。這不是你想要的語法。 C++中存在構造函數的原因很充分,除非有很好的理由規避它們,否則應該使用它們。

如果你不能使用構造出於某種原因,然後使用一個工廠函數:

Bar *baz = Bar::initWithPuppies(); 

它會做的對象分配和初始化,所以你不必直接使用new

至於該錯誤的原因,那是因爲你不能隱式升頻。所有Bar對象也是Base對象,由繼承的性質。因此,C++會隱式地將指向派生類的指針轉換爲指向基類的指針。相反的是真:Base自動所有Bar類。因此,C++會正確地給你一個嘗試轉換繼承層次結構的錯誤。

你必須明確地使用dynamic_cast做這種轉換。你可以使用C樣式轉換或static_cast,但如果你絕對肯定該類型是你希望它是什麼樣的纔有效。

+0

所以,你不必明確地使用'dynamic_cast'(事實上在這種情況下你不能這樣做,因爲'Base'沒有虛函數)。 –

+0

那麼基類中的initWithPuppies如何知道如何創建一個沒有類型轉換的派生? – Jay