当班级想要结合

时间:2010-09-06 16:08:38

标签: c++ class coupling

我遇到了两个曾经很好分开的课程的问题,但现在他们想要结合。

没有过多地讨论问题的具体细节,这里是:

我曾经有一个包含3个空间位置顶点的类Triangle。

class Triangle
{
    Vertex a,b,c ; // vertices a, b and c
} ;

程序中有许多Triangle实例,因此每个实例都保留了自己的顶点副本。 getArea()getCentroid()等成员函数在类Triangle中编写,并且由于每个Triangle实例都具有顶点a,b和c的副本,因此找到区域或质心不依赖其他班级。应该如此!

然后我想转移到顶点数组/索引缓冲区样式表示,原因有其他原因。这意味着所有顶点都存储在位于Scene对象中的单个数组中,并且每个Triangle仅保留Scene中顶点的参考,而不是顶点本身的副本。起初,我尝试切换指针:

class Scene
{
    std::vector<Vertex> masterVertexList ;
} ;

class Triangle
{
    Vertex *a,*b,*c ; // vertices a, b and c are pointers
    // into the Scene object's master vertex list
} ;

(如果你想知道它的好处,我之所以这么做,主要是因为共享顶点的三角形。如果* a移动,那么使用该顶点的所有三角形都会自动更新。)

这将是一个非常好的解决方案!但它没有可靠地工作,because std::vector invalidates pointers,我在类Scene中使用std :: vector作为主顶点列表。

所以我不得不使用整数:

class Triangle
{
    int a,b,c ; // integer index values
    // into the Scene object's master vertex list
} ;

但现在我遇到了这个新的耦合问题:要找到自己的区域或质心,类Triangle需要访问class Scene之前没有的地方。好像我已经把事情搞砸了,但事实并非如此。

WWYD?

7 个答案:

答案 0 :(得分:4)

为什么不让vector中的Scene只存储指针?

std::vector<Vertex *> masterVertexList;

这样,Triangle可以继续使用Vertex *,你所要做的就是确保在Scene的析构函数中删除指针。

答案 1 :(得分:4)

您可以将向量传递给构造函数中的三角形,以便它可以保持对向量的引用。然后它不需要访问或了解场景。

typedef std::vector<Vertex> VertexContainer;

class Scene
{
    VertexContainer  masterVertexList ;  
} ;  

class Triangle  
{  
    // A references to the vertices contained in Scene.
    // A triangle no longer needs to know anything about a scene
    VertexContainer&   vertexListRef;

    // index into vertexListRef of the triangles points.
    VertexContainer::size_type  a;
    VertexContainer::size_type  b;
    VertexContainer::size_type  c;  

    public:
        Triangle(VertexContainer&           masterVertexList,
                 VertexContainer::size_type x,
                 VertexContainer::size_type y,
                 VertexContainer::size_type z)
            :vertexListRef(masterVertexList)
            ,a(x),b(y),c(z)
        {}
};

答案 2 :(得分:3)

在我看来,你的三角形确实依赖于你的场景(因为它的顶点都是那个特定场景的成员),所以让对象这样做是没有羞耻感的。事实上,我可能会给Triangle一个强制性的场景*成员。

答案 3 :(得分:1)

从非耦合到耦合的变化是您决定在可能的情况下共享顶点的自然结果。以前,每个三角形“拥有”它的顶点,并且场景(可能)拥有一堆或三角形。

允许三角形共享顶点会改变基本模型 - 当/如果顶点可能在两个或更多个三角形之间共享时,任何一个三角形都不能再拥有该顶点。虽然可能(例如,使用类似于shared_ptr的东西)拥有分布式共享所有权方案,但您现在正在做的事情可能更简单:每个顶点仍然只有一个所有者,但是所有者现在是场景而不是单个三角形。

由于三角形现在只是一种方便的方法,可以在“拥有”集合中对某些顶点进行分组,而不是拥有顶点本身,因此三角形与拥有顶点的集合之间存在更紧密的耦合也就不足为奇了。如果您非常关心它,您仍然可以隐藏共享所有权,以至少保留以前松散耦合的外观。

一般的想法很简单:不是每个三角知道保存三角形顶点的场景,而是创建一个顶点代理类,它结合了场景ID和顶点索引,因此三角形可以操纵顶点代理对象就像以前会有顶点对象一样。你并没有完全消除更紧密的耦合,但你将更紧密耦合的“知识”与单个类隔离开来,只有 负责保持松散耦合的外观。

明显的缺点是顶点代理对象可能存储了大量冗余数据。例如,任何特定三角形中的所有顶点代理都清楚地表示同一场景中的顶点。如果为每个顶点代理显式存储场景ID,则存储场景ID的三个副本,而不是之前的场景ID。有时这是值得的 - 有些则不是。如果这是一个真正的问题,你可以尝试一种避免明确存储场景ID的方法,但这可能会涉及一些与语言无关(甚至接近)的技巧。

答案 4 :(得分:1)

如果您只是添加或删除顶点列表的末尾,请改用deque

答案 5 :(得分:1)

我不认为这太糟糕了。 Triangle失去了一些普遍性并成为Scene的外围类,但如果它不被用作外部接口(并且与内部缓冲区的那种联系不建议),那只是一种自然演变。

我的解决方案与您的解决方案相似,但含糖量更高。

struct Triangle
{
    Triangle( ... ) { ... }
    Vertex *a(),*b(),*c() ; // trivia: this is valid syntax! Getters adjust…
private:
    size_t ax, bx, cx; // … offsets…
    Scene *client; // … into the Scene object's master vertex list.
} ;

这样,您不必重新组织内存中的内容,只需要调整旧代码就需要将()添加到->a.a等,这可以通过搜索来完成 - 并且替换,并且无论如何改进OO风格。

或者,取消构造函数和private并将其设为POD。

答案 6 :(得分:0)

假设您只有一个Scene,可以将其设为单个对象,并通过静态方法访问顶点列表。

如果您有多个Scene个对象,则每个Triangle只属于一个Scene - 它应该“知道”它属于哪个场景。因此,您应该使用Scene引用初始化每个Triangle,并将其存储为类成员。