2

我有一点概念上的问题。我有不同的类来表示边缘的几何数据,具体取决于边缘的类型。例如直线和圆的类:

class Line{
private: 
    double[3] startPoint;
    double[3] endPoint;
public:
    //getter and setter and some other functions such as equals

}

class Circle{
private: 
    double[3] center;
    double[3] planeNormal;
    double    radius;
public:
    //getter and setter and some other functions such as equals   
}

现在我需要一个Edge存储边缘类型和拟合几何数据的类。最后边缘必须存储在一个std::vector<Edge> edges;问题是我在运行之前不知道类型,因为我正在分析可以具有各种类型边缘的 CAD 零件的边界表示。

class Edge{
private:
    EdgeType type;
    GeometricData data;
public:
    //...
}

那么我应该如何设计我的class Edge和特别GeometricData是必须存储一个Line-object、一个-object 或另一个几何对象,Circle以便我可以从GeometricDatato或者它可能是任何几何类返回。LineCircle

  • 我尝试使用GeometricData作为基类的多态性,但是派生类太不同了,因为还包括了诸如 B-Splines 之类的东西。
  • 我还尝试了 set 和 get 方法的GeometricDataasvoid*和模板方法,但是由于对象的生命周期(我必须递归地分析 BRep),我在存储数据而不仅仅是指针时遇到了问题。

只要我可以使用 -vector 访问诸如startPoint直线或radius圆的类型拟合数据,我也将不胜感激可能会改变几何表示的整个概念的建议edges

编辑:感谢您的快速响应。我决定使用 suszterpatt 建议,包括我的一些模板,并将我的更改std::vector<Edge>std::vector<shared_ptr<Edge>>TAS 提到的。现在看起来像这样:

#include "stdafx.h"
#include <string>
#include <sstream>
#include <iostream>
#include <vector>

using namespace std;

enum EdgeType{
    LINE = 100,
    CIRCLE
};

//Basis
class GeometricData {
private:
public:
    virtual string toXMLString() = 0;
};

class Line : public GeometricData{
//less code just for illustration
private:
    double d1;
public:
    double getD1() { return d1; }    
    void setD1(double d1) { this->d1 = d1;}
    virtual string toXMLString() {
        stringstream s;
        s << "d1=\"" << d1 <<"\"";
        return s.str();
    }
};

class Circle : public GeometricData{
private:
    double d2;
public:
    double getD2() { return d2; }
    void setD2(double d2) { this->d2 = d2;}
    virtual string toXMLString() {
        stringstream s;
        s << "d2=\"" << d2<<"\"";
        return s.str();
    }
};

class Edge{
private:
    EdgeType t;
    GeometricData* d;
public:
    Edge () { d = 0;}
    ~Edge () {if (d) {delete d; d=0;}}
    template <typename T> int   setGeomData (T data) {
        static_assert(
            is_same<T,Line*>::value || 
            is_same<T,Circle*>::value,
            "EdgeGeometryType is not supported");


        GeometricData* buffer = data;
            //set type corresponding to thethis->data given= data

            if(is_same<T,Line*>::value){
                this->t = LINE;
                Line* lb = dynamic_cast<Line*>(buffer);
                Line* l = new Line(*lb);
                this->d = l;
            }else if (is_same<T,Circle*>::value){
                this->t = CIRCLE;
                Circle* cb = dynamic_cast<Circle*>(buffer);
                Circle* c = new Circle(*cb);
                this->d = c;
            }else{// this case should not occure because of the static_assert
                return -1;
            }
            return 0;

    };
    template <typename T> T getGeomData () {
        static_assert(
            is_same<T,Line*>::value || 
            is_same<T,Circle*>::value, 
            "EdgeGeometryType is not supported");

        if ((this->t == LINE        && is_same<T,Line*>::value) || 
            (this->t == CIRCLE      && is_same<T,Circle*>::value))
        {
            return dynamic_cast<T>(this->d);
        }else{
            return NULL;
        }
    };
    EdgeType getType(){ return t; }
    //void setType(EdgeType t) { this->t = t; } not needed
    GeometricData* getData(){return d;}
};

class Model {
private:
    vector <shared_ptr<Edge>> edges;
public:
    Model(){}
    vector <shared_ptr<Edge>> getEdges(){ return edges; }
    void addEdge (Edge* e) {edges.push_back(shared_ptr<Edge>(e));}
    shared_ptr<Edge> getEdge(int i ){ return edges.at(i); }
};

// Functions
void foo2 (Edge* e){
    Line* l = new Line; 
    l->setD1(0.1);
    e->setGeomData<Line*>(l);
    //e->setType(LINE);   not needed
    delete l;
}
void foo1 (Edge* e){
    Circle c;
    c.setD2(0.2);
    e->setGeomData<Circle*>(&c);
    //e->setType(CIRCLE);  not needed
}
void foo (Model* mdl){
    Edge* e1 = new Edge;
    Edge* e2 = new Edge;
    foo1(e1);
    foo2(e2);
    mdl->addEdge(e1);
    mdl->addEdge(e2);
}   
int _tmain(int argc, _TCHAR* argv[])
{
    Model mdl;
    int i;
    foo(&mdl);
    cout << "Edge 1: " << mdl.getEdge(0)->getData()->toXMLString() << endl;
    cout << "Edge 2: " << mdl.getEdge(1)->getData()->toXMLString() << endl;
    for (i = 0; i<2; i++){
        switch (mdl.getEdge(i)->getType()){
            case LINE: {
                Line* ld = (mdl.getEdge(i)->getGeomData<Line*>());
                cout << "Line (templated get): " << ld->getD1() << endl;
            }break;
            case CIRCLE:{
                Circle* cr = (mdl.getEdge(i)->getGeomData<Circle*>());
                cout << "Circle (templated get): "<< cr->getD2() << endl;
            }break;
        }   
    }
    return 0;
}
4

3 回答 3

2

有很多解决方案。似乎最适合的一个是Boost.Variant;如您所展示的那样定义您的LineCircle类,然后创建GeometricData一个 typedef variant<Line, Circle>,您将能够在其中存储任何一个的实例。当你想从 a 返回GeometricData到实际存储的对象时,你可以执行 cast,或者你可以编写一个所谓的访问者。访问者只是一个类,为每种可能的类型指定一个动作,然后boost::apply_visitor可用于根据存储的内容选择正确的动作。

示例(使用向量进行更简单的表示法):

struct Line {
    Vector3d startPoint, endPoint;
};

struct Circle {
    Vector3d center;
    float radius;
};

using GeometricData = boost::variant<Line, Circle>;

struct MidpointVisitor : boost::static_visitor<Vector3d> const {
    Vector3d operator()(Line const& line) {
        return (line.startPoint + line.endPoint)/2;
    }

    Vector3d operator()(Circle const& circle) const {
        return circle.center;
    }
};

void foo() {
    GeometricData data;
    // ...
    auto midpoint = boost::apply_visitor(MidpointVisitor{}, data);
    // ...
}

类型严格较少的解决方案是Boost.Any,但我认为这种情况没有任何优势。即使您确实需要另一个选项,您也可能希望明确指定。

我怀疑您使用void*(或使用通用基类和 RTTI)的解决方案可以使用智能指针来工作。但是,我能看到的唯一优点是编译速度更快,编译器错误消息更少,而您最终不得不为动态分配而烦恼,并且无法访问。

你也可以为此推出你自己的联合,有效地实现一些类似于 Variant 的东西。这将涉及确保你得到正确的构造、破坏和对齐,并且不会触发一些模糊的未定义行为的情况。如果这对您来说不是问题并且您真的不想使用库,那么这是一种选择,但它在很大程度上是在重新发明轮子。

于 2013-10-20T18:21:56.490 回答
1

我会说多态性,其中共享接口可能看起来像这样:

class Edge
{
    enum EdgeType
    {
        CIRCLE,
        LINE
    };

    EdgeType GetType();
}

然后在某处的 switch 语句中,您可以执行以下操作:

switch (myEdge.GetType())
{
    case Edge::EdgeType::CIRCLE:
        auto myCircle = (Circle)myEdge;
        // do things specific to circle
        break;
    case Edge::EdgeType::LINE:
        auto myLine = (Line)myEdge;
        // do things specific to line
        break;
}

话虽如此,我会尝试在 switch 语句上尽可能多地使用多态性,但是上面的接口为您提供了使用边的函数的选项,该函数包含基于类型执行不同操作的逻辑。

于 2013-10-20T18:39:46.203 回答
0

我不确定我是否完全理解您正在尝试解决的问题,但是通过阅读和理解问题,我想说看看序列化 您可以创建一个全局数组类型变量,存储您需要的对象,将其序列化为反序列化当你需要使用它时。

于 2013-10-20T18:30:05.333 回答