我有一点概念问题。我有不同的类代表边缘的几何数据,这取决于它的边缘类型。例如,直线和圆的类:
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
来存储边的类型和拟合几何数据。
最后,Edge必须存储在std::vector<Edge> edges;
中。问题是我在运行之前不知道类型,因为我正在分析可能有各种类型边缘的CAD零件的边界表示。
class Edge{
private:
EdgeType type;
GeometricData data;
public:
//...
}
那么我应该如何设计我的class Edge
和espacially GeometricData
,它必须存储Line
- 对象,Circle
- 对象或其他几何对象,以便我可以从GeometricData
返回Line
,Circle
或任何几何等级。
GeometricData
作为基类的多态,但是派生了
因为像B-Splines这样的东西也是如此,所以课程太不同了
包括在内。 GeometricData
作为void*
和模板方法
对于set-and get-methode,但我有问题
由于生命周期,存储数据而不仅仅是指针
对象(我必须递归地分析BRep)。我也很感激可能会改变几何表示的整个概念的建议,只要我可以访问类型拟合数据,例如直线的startPoint
或圆的radius
使用edges
- vector。
编辑:
感谢您的快速回复。我决定使用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;
}
答案 0 :(得分:2)
有许多解决方案。似乎最合适的是Boost.Variant;在您展示时定义您的Line
和Circle
类,然后将GeometricData
设为variant<Line, Circle>
的typedef,并且您将能够存储任何一个的实例那里。当您想要从GeometricData
返回到存储的实际对象时,您可以perform a cast,或者您可以编写所谓的visitor。访问者只是指定每种可能类型的操作的类,然后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的一些东西。这将涉及确保您的构造,破坏和对齐都正确,并且不会触发一些未定义行为的模糊情况。如果这对你来说不是问题而你真的不想使用图书馆,那么它就是一个选择,但它正在重新发明轮子。
答案 1 :(得分: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语句中使用多态,但是上面的接口让你可以选择让一个使用edge的函数包含根据类型做不同事情的逻辑。
答案 2 :(得分:0)
我不确定我是否完全理解你正在尝试解决的问题,但是通过阅读和理解问题,我会说明一下serialization 您可以创建一个全局数组类型变量,存储您需要的对象,序列化它,并在需要时对其进行反序列化。