2017-05-29 30 views
1

讓說我有下面的代碼:C++ - 抽象類和替代虛擬構造

class Block{ 
private: 
    data Data; 
public: 
    data getData(); 
    Block(arg3 Arg3, arg4 Arg4); 
}; 

其實,有幾種方法可以建立一個塊,但總是以相同的成員數據和方法的getData() ,唯一的區別是如何構建塊。換句話說,唯一的區別是構造函數...

而不是爲每個構建過程編寫一個不同的類,我可以分解我的代碼的一部分,定義和聲明抽象類中的getData,如果有這樣的事情作爲C++中的一個虛擬構造函數,我可以針對與不同構建過程相對應的每個派生類編寫不同的代碼。

我對這種事情沒有太多經驗,所以我想知道是否有虛擬構造函數的替代方案?或者可能是一種不同的方式來做這種分解?

PS:我知道https://isocpp.org/wiki/faq/virtual-functions#virtual-ctors,但它看起來相當複雜,我想做什麼,這似乎很常見......我只是想分解幾個類之間的共享代碼,除了構造函數之外,它們對應於一切。我想強制新的類對應於其他構建過程來實現一個新的構造函數。

更多關於我的特殊情況細節:

我有一個算法,其中我用塊,它不依賴於他們的建設過程中,所以我一直在使用一個模板參數漠然表示塊實現的算法其建設過程。但是我使用了一些方法及其構造函數,所以我需要表示塊的類都具有我需要的相同類型的方法,以及使用它們作爲我的算法實現的模板參數的相同構造函數。這就是爲什麼我想到抽象類,強制一個新實現的代表塊的類在我實現的算法中擁有我需要的方法和構造方法。可能這是一個糟糕的設計模式,這就是爲什麼我堅持......

編輯

謝謝你的答案爲止。我試圖成爲一個通用的,但我覺得它實際上太模糊了,即使我最後提供的細節。所以這是我想的事:我有一個矩陣類如下

// Matrix.hpp 
template<typename GenericBlock> class Matrix{ 
    std::vector<GenericBlock> blocks; 
    Matrix(arg1 Arg1, arg2 Arg2); 
}; 

template<typename GenericBlock> 
Matrix<GenericBlock>::Matrix(arg1 Arg1, arg2 Arg2){ 
    // Do stuff 
    GenericBlock B(arg3 Arg3, arg4 Arg4); 
    B.getData(); 
} 

塊實際上是壓縮,且存在多種方式對其進行壓縮,並沒有在課堂Matrix任何改變。爲避免爲每個壓縮工藝編寫一個矩陣類,我使用了一個模板參數。所以我只需要爲每個壓縮工藝編寫一個類,但它們必須具有相同的方法和構造函數參數才能與Matrix兼容。

這就是爲什麼我想到做一個抽象類,爲每個壓縮工藝編寫一個類。在抽象類中,我會寫出Matrix中所需的所有內容,以便每個派生類都可以與Matrix兼容。我的例子中現在的問題是:我可以在抽象類中定義getData,因爲它總是相同的(例如,Data可以是行數)。派生類唯一需要定義的就是構造函數。

一個解決方案將是沒有抽象類和使用受保護的構造函數可能。但它不強制新派生類重新實現構造函數。這就是我被卡住的原因。但是我認爲這個問題已經足夠通用以引起其他人的興趣。那麼在這種情況下,有沒有其他的虛擬構造函數? (可能是工廠模式,但對於這樣的常見問題似乎相當複雜)如果不是,是否有更好的方法來實現一個矩陣類,其塊可以以不同的方式構建,即其構造函數可以彼此不同,而有相同的數據和一些共同的方法? PS:我對產生低秩矩陣的壓縮技術感興趣,這就是爲什麼數據總是相同的原因,而不是構建過程。

+3

爲什麼不只是有多個構造函數?在我看來,你是過於複雜的事情。 –

+1

如果您想在運行時配置模塊構建策略 - 爲什麼不使用抽象工廠? –

+0

@JesperJuhl你是什麼意思? –

回答

2

從目前共享的內容來看,目前尚不清楚爲什麼需要抽象類或虛擬構造函數。每種構建塊的方式的工廠函數將執行:

class Block { 
    Data data; 
public: 
    Block(Data d) : data(std::move(d)) {} 
    Data getData(); 
}; 

Block createABlock() { return Block{Data{1.0, 2.0, 3.0}}; } 
Block createBBlock() { return Block{Data{42.0, 3.14, 11.6}}; } 

int main() { 
    auto b1 = createABlock(); 
    auto b2 = createBBlock(); 
} 

Live demo

也許這需要與抽象工廠進行擴展,因此您可以繞過通用塊廠:

using BlockFactory = std::function<Block()>; 

int main() { 
    BlockFactory f = createABlock; 
    auto b3 = f(); 
} 

編輯: 關於你的編輯,你有什麼建議works fine。你不需要一個虛擬構造函數。模板類型GenericBlock只需滿足由模板定義的隱式接口。它不需要從特定的基類派生(儘管它可以)。它唯一需要的是它必須有一個構造函數,它需要一組特定的參數和一個方法。你所擁有的是編譯時靜態多態,虛函數是針對運行時動態多態的。

繼承將正常工作,但正如我上面所說,我會試圖使用某種工廠。您可能不需要對整個Matrix類進行模板化,因爲只有構造函數需要工廠。如果工廠在編譯時已知這可以作爲一個模板參數傳遞:

class Matrix { 
    std::vector<Block> blocks; 
public: 

    template<typename BlockFactory> 
    Matrix(BlockFactory f); 
}; 

template<typename BlockFactory> 
Matrix::Matrix(BlockFactory f){ 

    // Do stuff... 

    Block B = f(); 
    auto data = B.getData(); 
    for (auto v : data) 
    std::cout << v << " "; 
    std::cout << "\n"; 
} 

int main() { 
    Matrix ma(createABlock); 
    Matrix mb(createBBlock); 
} 

Live demo

+1

謝謝你的回答。我想迫使新派生的類以「矩陣」類中需要的方式定義構造函數,這就是爲什麼我想到「虛構構造函數」(不存在)的原因。但正如你所說,無論如何,GenericBlock必須滿足隱式接口,我想我是過於複雜的東西。我最終做了你在編輯中建議的內容。 –

1

TL:DR,但是如果data對於所有Block都是相同的,那麼您甚至不需要多個類,但只需要多個構造函數。

class Block 
{ 
    enum { type1, type2, type3 }; 
    int type; 
    data Data; 
public: 
    Block(int x) 
    : type(type1), Data(x) {} 
    Block(std::string const& str) 
    : type(type2), Data(str) {} 
    Block(data const*x) 
    : type(type3), Data(data) {} 
    /* ... */ 
}; 
+0

我需要構造函數具有相同的參數,實際上,所有構建過程都採用相同的參數。 –

+0

正如我在我的問題結束時所說的,我想將這些塊用作我實現的算法的模板參數,以便它們需要具有構造函數的相同參數列表。 –

+3

@PierreMarchand只有現在我閱讀你的編輯和最新評論。 tbh我根本聽不懂。在調用構造函數時,你的算法如何不依賴於塊的構建方式,並且這個調用對於所有類型的塊應該是相同的......在什麼時候決定應該創建哪種類型的塊? – user463035818

0

沒有虛擬構造函數的替代品,因爲沒有虛擬構造函數。我知道這很難被接受,但這是事實。

總之,你不需要像如果它存在一個虛擬的構造會是什麼東西....

[..]唯一的區別是如何構建塊。換句話說,在 唯一的區別是構造...

如果唯一的區別是在構造函數,然後簡單地使構造帶,告訴需要什麼類型的塊的參數。或者你可以有一些功能是構建塊以不同的方式:

struct Block { 
    private: 
     Block(){} 
     friend Block createWoodenBlock(); 
     friend Block createStoneBlock(); 
}; 

Block createWoodenBlock(){ return Block(); } 
Block createStoneBlock(){ return Block(); } 


int main() { 
    Block woody = createWoodenBlock(); 
    Block stony = createStoneBlock(); 
} 
1
template<class T>struct tag_t{constexpr tag_t(){}; usong type=T;}; 
template<class T>constexpr tag_t<T> tag{}; 

這可以讓你通過類型的值。

struct BlockA{}; 
struct BlockB{}; 

class Block { 
    enum BlockType { typeA, typeB };; 
    BlockType type; 
    data Data; 
public: 
    Block(tag_t<BlockA>, int x) 
    : type(typeA), Data(x) {} 
    Block(tag_t<BlockB>, int x) 
    : type(typeB), Data(2*x+7) {} 
/* ... */ 
}; 

這些塊都是相同的類型。標籤決定了它們的構造方式。