2016-07-04 10 views
2

私は、3D形状の派生クラス、すなわちBallまたはTetraederを持つシェイプという名前の基本クラスを持っています。 私のプログラムは、図形の種類とそのパラメータをテキストファイルから読み込み、ボリュームと領域を出力テキストに書き込む必要があります。テキストファイルから名前を読み取ってオブジェクトを構築します

#include <fstream> 
#include <string> 
#include <sstream> 
#include <iostream> 
#include <cstring> 
#include <cstdlib> 
#include "Shape.h" 
#include "Ball.h" 
#include <vector> 

using namespace std; 

int 
main(int argc, char** argv) 
{ 
string input = string(argv[1]); 
string output = string(argv[2]); 

ifstream file(input); 
string line; 

string shapename; 
int nx = atoi(argv[3]); 
int ny = atoi(argv[4]); 
int nz = atoi(argv[5]); 
while (std::getline(file, line)) 
{ 
    std::stringstream lineStream(line); 

    lineStream >> shapename; 
    int value; 
    std::vector<int> lineData; 
    while (lineStream >> value) 
    { 
     lineData.push_back(value); 
    } 

    Shape * objShape = new shapename(lineData); 
    objShape -> calc_volume; 
    objShape -> calc_projection(nx,ny,nz); 

    std::ofstream f(output); 
    f << objShape -> get_volume() << " " << objShape -> get_projection << endl; 
} 


} 

私の質問は、テキストファイル内の文字列から、特に派生クラスをすべて知らなくてもオブジェクトを作成する方法です。

コードを変更することなく、新しいファイルを追加するだけで、プログラムにさらに多くの図形を追加することができます。

+0

Googleの工場パターン。 – drescherjm

答えて

2

質問です:

私の質問はどのように私は特に、すべての派生クラスを知らなくても、 テキストファイル内の文字列からオブジェクトを作成することができ、今です。

答えは、すべての派生クラスを知る必要があります。

C++ does not have reflection。そのように、すべてのクラス名は、コンパイル時にバインドされ、工場のこの種のいくつかのバリエーションを行うしかないされています

if (name == "box") 
    return new Box(); 
else if (name == "circle") 
    return new Circle(); 

// ... etc ... etc ... 

の一部を自動化することを可能にする様々な異なるアプローチとデザインパターンがあります。すべてのサブクラスのハードコードされたリストを明示的に維持しなくても済むように、これを柔軟にする必要があります。

私は簡単で簡単なアプローチを概説します。私はこれまでに使用していた非常に単純なもので、同じ結果を得ています。ファクトリを手動で編集する必要がない方法で、指定されたサブクラスを名前でインスタンス化し、いくつかのコード行。新しいサブクラスのファクトリを作成するプロセス全体を、新しいサブクラスを作成するプロセスにきちんとラップすることができ、非常に弾力のある区画化されたソリューションになります。

は、これらのサブクラスのファクトリを登録するための簡単な仕組みを考えてみましょう:

typedef Shape (*shape_factory_t)(); 

Shapeは、図形のあなたのスーパークラスです。だから今、あなたがすべてのあなたの工場のマップを持って

std::map<std::string, shape_factory_t> all_factories; 

void register_factory(const std::string &name, shape_factory_t factory) 
{ 
    all_factories[name]=factory; 
} 

工場はこのような何かを働くだろう。

auto iter=all_factories.find(name); 

if (iter == all_factories.end()) 
    throw; // Some exception, unknown subclass 

return (*iter->second)(); 

すべての権利、その部分の世話の:代わりに無限のif声明のあなたは、クラス名で検索して、適切なファクトリを呼び出すことができ、単一のマップ、のようなものを持っています。この問題は、各サブクラスのファクトリを登録する方法になります。

のは、あなたがサークルの実装を持っているとしましょう:

class Circle : public Shape { 

    class initializer; 

// ... other things that make up the Circle 

}; 

その後、circle.cppに、このサブクラス実装する:このように

static Shape *create_circle() 
{ 
    return new Circle(); // Add constructor parameters, as appropriate 
} 

class Circle::initializer { 

public: 
    initializer() { 
     register_factory("circle", create_circle); 
    } 
}; 

static initializer initialize_me; 

を、Circleクラスはその工場に自分自身を登録与えられたShapeのインスタンスをクラス名で作成します。主な工場コードに触れることなく、個別に、すべての他のサブクラスを進めて実装することができます。 Boxサブクラスを同じ方法で宣言し、ファクトリに自身を登録させると、クラス(おそらくcreate_box()関数を呼び出すことによって)を作成することを自動的に知ることができます。

初期化の順序は、もう1つ注意が必要です。ご存じのように、異なる翻訳単位のグローバルスコープオブジェクトの相対的な初期化順序は、実装定義であり、そうでなければC++では指定されていません。

すべてのファクトリ関数のグローバルstd::mapは、アプリケーションの起動時に、すべてのサブクラスが自分自身を登録してマップに配置しようとする前に構築する必要があります。

これはかなり一般的なstatic initialization order fiasco質問ですが、いくつかの既知の解決策があります。 in this answerがうまくいくはずです。

0

C++はあまり柔軟ではありません。新しいシェイプを追加すると、新しいクラスを追加することになります(Shapes、Ball、Tetraederクラスをすでに作成しているので、クラスを増やしたいと思っています)。新しいクラスを追加する場合は、コードを変更する必要があります。つまり、再コンパイルする必要があります。

には、派生クラスが何であるかを知るためにがあります。あなたはそれらをコードする人なので、あなたもそれらのリストを持っているかもしれません。あなたのプログラムがフレキシブルであることのためにできる最良のことは、あなたが既にやっているように見えるヘッダファイルを使用することです。テキストファイル内の文字列からオブジェクトを作成するためとして

(あなたは3Dオブジェクトのクラスが何であるかを知っている間)、あなたは文字列を解析することができ、それを行ってからかなり単純な何かをしたい形状の種類を読みますこのような:

//shapeType - a string containing the type of the 3D object 

Shape *newShape; 

switch(shapeType) { 
case "ball": 
    newShape = new Ball(...); // ... - parameters for the ball dimensions 
    break; 
case "tetraeder": 
    newShape = new Tetraeder(...); // ... - parameters again 
    break; 
default: 
    return -1; 
} 

//and now you can use newShape as you wish