C++ 如何在一个变量中存储不同的类?

C++ 如何在一个变量中存储不同的类?,c++,conceptual,C++,Conceptual,我在概念上有点问题。我有不同的类来表示边的几何数据,这取决于边的类型。例如,直线和圆的类: 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

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

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 edges中问题是我在运行前不知道类型,因为我正在分析CAD零件的边界表示,这些零件可能具有各种类型的边

class Edge{
private:
    EdgeType type;
    GeometricData data;
public:
    //...
}
因此,我应该如何设计我的
类边
,特别是
几何CDATA
,它必须存储一个
-对象、
-对象或另一个几何对象,以便我可以从
几何CDATA
返回到
或任何可能的几何类

  • 我尝试了使用
    geometricATA
    作为基类的多态性,但是 类太不一样了,因为像B样条这样的东西也是一样的 包括在内
  • 我还尝试了
    GeometricData
    作为
    void*
    和模板方法 对于set-and-get方法,我有一些问题 存储数据,而不仅仅是指针,因为生命周期 对象(我必须递归地分析BRep)
只要我能使用
-向量访问类型拟合数据,如直线的
起点
,或圆的
半径
,我还希望得到可能改变几何表示的整个概念的建议

编辑: 谢谢你的快速回复。我决定使用suszterpatt建议,包括我的一些模板,并将我的
std::vector
更改为
std::vector
。现在看起来是这样的:

#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;
}
class Edge
{
    enum EdgeType
    {
        CIRCLE,
        LINE
    };

    EdgeType GetType();
}
#包括“stdafx.h”
#包括
#包括
#包括
#包括
使用名称空间std;
枚举边类型{
直线=100,
圆圈
};
//基础
类几何体CDATA{
私人:
公众:
虚拟字符串toXMLString()=0;
};
类行:公共几何体CDATA{
//更少的代码只是为了说明
私人:
双d1;
公众:
double getD1(){return d1;}
void setD1(双d1){this->d1=d1;}
虚拟字符串toXMLString(){
细绳;
d2=d2;}
虚拟字符串toXMLString(){
细绳;
s给定的数据=数据
如果(相同::值){
这->t=线;
行*lb=动态_转换(缓冲区);
行*l=新行(*lb);
这个->d=l;
}else if(相同::值){
这个->t=圆;
圆圈*cb=动态_转换(缓冲区);
圆圈*c=新圆圈(*cb);
这个->d=c;
}else{//由于静态_断言,不应发生这种情况
返回-1;
}
返回0;
};
模板T getGeomData(){
静态断言(
|u相同::值||
是相同的::值,
“不支持EdgeGeometryType”);
如果((this->t==LINE&&is_-same::value)|
(此->t==圆和圆是相同的::值)
{
返回动态_cast(此->d);
}否则{
返回NULL;
}
};
EdgetType getType(){return t;}
//void setType(EdgeType t){this->t=t;}不需要
GeometricATA*getData(){return d;}
};
类模型{
私人:
矢量边;
公众:
模型(){}
向量GetEdge(){返回边;}
void addEdge(Edge*e){edges.push_back(shared_ptr(e));}
shared_ptr getEdge(inti){returnedges.at(i);}
};
//功能
void foo2(边*e){
行*l=新行;
l->setD1(0.1);
e->setGeomData(l);
//e->setType(行);不需要
删除l;
}
void foo1(边*e){
圈c;
c、 setD2(0.2);
e->setGeomData&c;
//e->setType(圆形);不需要
}
void foo(型号*mdl){
边*e1=新边;
边*e2=新边;
foo1(e1);
foo2(e2);
mdl->addEdge(e1);
mdl->addEdge(e2);
}   
int _tmain(int argc,_TCHAR*argv[]
{
模型mdl;
int i;
foo&mdl;

cout toXMLString()有很多解决方案。最适合的解决方案是:按照所示定义
类,然后将
几何CDATA
制作为
变量
的typedef,这样就可以在其中存储其中任何一个的实例。当您想从
几何CDATA
返回到t时访问者只是一个类,为每种可能的类型指定一个操作,然后可以根据存储的内容选择正确的操作

示例(使用向量表示更简单的符号):

struct行{
矢量3D起点、终点;
};
结构圆{
矢量三维中心;
浮动半径;
};
使用geometricATA=boost::variant;
结构中点访问者:boost::static_visitor const{
Vector3d运算符()(行常量和行){
返回(line.startPoint+line.endPoint)/2;
}
Vector3d运算符()(圆常量和圆)常量{
返回圆中心;
}
};
void foo(){
几何数据;
// ...
auto midpoint=boost::apply_visitor(MidpointVisitor{},data);
// ...
}
一个类型不太严格的解决方案是,但我不认为这种情况有任何好处。即使您确实需要另一个选项,您也可能希望显式地指定它

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

您也可以为此推出自己的联盟,并有效实施
class Edge
{
    enum EdgeType
    {
        CIRCLE,
        LINE
    };

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