2017-09-14 186 views
1

我有一個類似的基類(但下面不編譯):從基類INITIALISE派生類

struct Base { 
    virtual void foo()=0; 

    virtual void init() 
    { 
     using Derived = typename std::remove_reference<decltype(*this)>::type; 
     *this = Derived(); // does not compile as Base is an abstract class 
    } 
}; 

Base有很多派生類的,什麼我想要做的就是讓所有的派生類有這init()函數繼承自Base並初始化自己(通過調用它們的構造函數)。如果他們願意,也可以讓他們覆蓋init()

問題:

  1. 我知道這不會編譯。如果我的基地不是抽象類,它會起作用嗎?我不知道this指針會被解釋爲派生對象指針嗎?

  2. 如何實現我想要什麼?

-------------------------------編輯---------- ----------------------------

澄清一點, init()函數實際上是在做重置。它將每個派生對象重置爲默認狀態。我希望它在Base類中具有這樣的默認行爲,並且如果派生類需要一些不同的reset(),它們可以自由覆蓋它。

+8

這聽起來不像一個好設計。 init函數引入了一個可能的故障點。爲什麼不使用在創建對象時總會觸發的構造函數? – NathanOliver

+1

@james我同意@NathanOliver。如果您自動獲得派生類構造,但仍然允許通過基類指針/引用進行多態,我不明白爲什麼需要'init'。 – VermillionAzure

+11

你能展示一個用例嗎?這感覺就像一個[XY問題](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)。如果你放棄這個錯誤的方法,完成你真正想要的東西可能會更容易。 – StoryTeller

回答

1

一些網友在評論中提到CRTP(奇異遞歸模板模式),沒有表現出一個具體的解決方案,所以這裏是一個:

template <typename CRTP> 
struct Base { 
    virtual void foo()=0; 

    virtual void init() 
    { 
    static_cast<CRTP&>(*this) = CRTP{}; 
    } 
}; 

struct Derived : public Base<Derived> { 
    void foo() override {} 
}; 
+0

正如我在評論中解釋的,我意識到CRTP。這種方法不僅需要修改Base類,還需要每個派生類,這在我的情況下是不可能的。 – james

+0

@james在'public Base'旁邊添加''到每個派生類已經太多了你的情況? – oLen

+0

是的,這對我來說已經太多了 - 有很多來自Base的類。 「太多」,我的意思不僅是修改每個派生類的工作,而且是爲了獲得許可。 – james

0

您想根據的類型調用不同的功能派生類。這是C++中的多態。你有你的處置通常嫌疑人:

  1. 有一個虛擬函數派生類覆蓋。即基於動態類型的分派。
  2. 以某種方式查找靜態類型並使用重載。即基於靜態類型的調度。

CRTP是完成第二種方法的一種方法,派生方說它是繼承點的真正類型。

一個非常哈克的方式來完成第二,這是明確的,我不推薦,但可能在一個不正常的組織需要在你沒有其他選擇:嘗試將dynamic_cast的各種選項爲派生類。這可能是脆弱的。但是,即使有復位功能,而不是使用構造和銷燬也是脆弱的。

我建議重新評估您對重置成員函數的需求,詢問您試圖用它來完成的工作,並儘量不要降低工作產品的質量,作爲與您的交互組織。 (我的觀點。)

0

選1/2

也許你可以執行你想要的行爲的模板功能。然後可以從每個需要默認行爲的派生類中明確調用它。通過使函數爲純虛擬的,確保它在每個派生類中實現。創建一個智能指針類,它包含一個指向原始對象的指針,以及一個指向可重新啓動對象的函數的指針。

#define DEFAULTINIT \ 
virtual void init()\ 
{\ 
    DefaultBehavior(this);\ 
}\ 

template <typename T> void DefaultBehavior(T *x) 
{ 
    *x = T(); 
} 
struct Base { 
    virtual void init() = 0; 
}; 

struct Derived : virtual public Base 
{ 
    DEFAULTINIT; 
}; 

選項2/2

創建具有指向原始對象,以及一個指針,它指向可以重新初始化該對象的功能的智能指針類。

struct Base { 
    virtual ~Base()=0; 
}; 

struct Derived : virtual public Base { 
    int a = {}; 
}; 


template <typename T> 
void default_initter(Base *b) 
{ 
    T *d = dynamic_cast<T *>(b); 
    *d = T(); 
} 

typedef void (*initter)(Base *); 
struct SmartPointer 
{ 
    template <typename T> 
    SmartPointer(T *d) : base(d), init(default_initter<T>) {}; 
    Base  * base; 
    initter init; 
}; 

#include <iostream> 

int main() 
{ 
    Derived *d = new Derived(); 
    SmartPointer *sp = new SmartPointer(d); 
    d->a = 5; 
    std::cout << d->a << std::endl; 
    sp->init(sp->base); 
    std::cout << d->a << std::endl; 
} 
+0

這種方法的問題是,每個派生都必須實現init()函數。那麼就沒有'默認行爲',因爲每個派生都有自己的實現。這就是爲什麼我希望它在Base類中,以便每個派生類都將繼承init()並自動具有「默認行爲」。 – james

+0

我添加了一個cpp宏,它將每個派生類中必須實現的代碼量減少到一行。 – Juan

+0

這沒有幫助,交配。如果你閱讀了oLen的回答和下面的評論,你就會明白爲什麼我不能採取這種方法。 – james