2017-05-29 5 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を知っていますが、私がやりたいことに関してはかなり複雑なようですが、かなり一般的です...私は、コンストラクタ以外のすべてに対応するいくつかのクラスの間で共有コードを分解したいだけです。そして私は、新しいコンストラクタを実装するために、他のビルドプロセスに対応する新しいクラスを強制したいと思います。私の特定の状況について

詳細:

私はブロックを使用するアルゴリズムを持っており、それが彼らの構築プロセスに依存しないので、私は淡々ブロックを表現するためにテンプレート引数を使用してアルゴリズムを実装しましたその建設プロセスのしかし、私はいくつかのメソッドとそのコンストラクタを使用するので、必要なものと同じ種類のメソッドを持つためにブロックを表すクラスと、アルゴリズム実装のテンプレート引数として同じコンストラクタを使用するクラスが必要です。それで、私が実装したアルゴリズムで必要なメソッドとコンストラクタをブロックに持つように新しく実装されたクラスに強制的に抽象クラスを考えさせたのです。それは悪いデザインパターンであると私は立ち往生午前理由があるかもしれ...

EDITは、これまでのあなたの答えをいただき、ありがとうございます。私は少し一般的なことをしようとしたが、私は最後に与えた細部でさえ、実際にはあまりにも曖昧であると感じる。だからここに私がやって考えたものである:

// 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と互換性があるように、私はMatrixに必要なものすべてを書きます。今の私の問題は私の例です:私はgetDataを抽象クラスに定義することができます。なぜなら、常に同じであるからです(たとえば、Dataは行数になります)。クラスから派生した唯一のものは、コンストラクタです。

解決策の1つは、抽象クラスを持たず、保護されたコンストラクタを使用することです。しかし、新たに派生したクラスがコンストラクタを再実装することは強制しません。それが私が立ち往生している理由です。しかし、私はこの問題は他の人にも興味があるほど一般的だと思う。この場合、仮想コンストラクタの代替手段はありますか? (工場のパターンかもしれませんが、そのような共通の問題ではかなり複雑です)そうでない場合は、ブロックが異なる方法で構築できるマトリックスクラスを実装するより良い方法があります。同じデータといくつかの方法が共通していますか?

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(); 
} 

EDIT:あなたのEDITについて 、あなたがworks fineを示唆している何を。仮想コンストラクタは必要ありません。テンプレートタイプGenericBlockは、テンプレートによって定義された暗黙のインタフェースを満たしていればよい。特定の基底クラスから派生する必要はありません(ただし可能です)。そのために必要な唯一のことは、特定の引数セットとメソッドを取るコンストラクタを持っていなければならず、getDataメソッドでなければならないということです。あなたが持っているのはコンパイル時の静的な多態性です。仮想関数は実行時の動的多型です。

継承は正常に機能しますが、上記のように私はある種の工場を使用するように誘惑されます。コンストラクタだけが工場を必要とするため、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

あなたの答えをありがとう。私は新しく派生したクラスに 'Matrix'クラスで必要だったようにコンストラクタを定義するように強制したかったので、私は"仮想コンストラクタ "(存在しない)を考えました。しかし、あなたが言ったように、 'GenericBlock'は暗黙的なインターフェースを満足しなければなりません。私はものを複雑にしていたと思います。私はEDITであなたが提案したことをやりました。 –

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今私はあなたの編集と最新のコメントを読んでいます。私は全く理解していません。あなたのアルゴリズムは、コンストラクタを呼び出すときにどのようにブロックがビルドされているかに依存しません。この呼び出しは、すべてのタイプのブロックで同じでなければなりません...どのポイントがどのタイプのブロックを作成すると決められますか? – 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) {} 
/* ... */ 
}; 

ブロックはすべて同じタイプです。タグは、タグの構成方法を決定します。

関連する問題