6

我对曾经很好分开的 2 个班级有疑问,但现在他们想结合。

无需过多讨论问题的细节,这里是:

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

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

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

然后,出于其他原因,我想转向顶点数组/索引缓冲区样式表示。这意味着所有顶点都存储在位于Scene对象中的单个数组中,并且每个Triangle顶点仅保留对 in 顶点的引用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 移动,则使用该顶点的所有三角形都会自动更新)。

这将是一个非常好的解决方案!但它不能可靠地工作,因为 std::vector 使指针无效,并且我在 class 中使用 std::vector 作为主顶点列表Scene

所以我不得不使用整数:

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

但是现在我遇到了这个新的耦合问题:要找到自己的区域或质心,类Triangle需要访问class Scene之前没有的地方。好像我已经 fsck`d 了一些东西,但不是真的。

世界青年会?

4

7 回答 7

4

为什么不也有vectorin Scenejust store 指针呢?

std::vector<Vertex *> masterVertexList;

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

于 2010-09-06T16:28:21.113 回答
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)
        {}
};
于 2010-09-06T17:45:34.827 回答
3

在我看来,您的三角形确实取决于您的场景(因为它的顶点都是该特定场景的所有成员),所以让对象这样做并不可耻。事实上,我可能会给 Triangle 一个强制性的 Scene* 成员。

于 2010-09-06T16:15:41.930 回答
1

从非耦合到耦合的变化是您决定尽可能共享顶点的自然结果。以前,每个三角形都“拥有”它的顶点,而场景(大概)拥有一堆或三角形。

允许三角形共享顶点改变了基本模型——当/如果一个顶点可能在两个或多个三角形之间共享时,没有一个三角形可以再拥有该顶点。虽然有可能(例如,使用 shared_ptr 之类的东西)有一个分布式的共享所有权方案,但您现在所做的可能更直接:每个顶点仍然有一个所有者,但所有者现在是场景而不是单个三角形。

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

总体思路相当简单:不是每个三角形都知道包含三角形顶点的场景,而是创建一个结合了场景 ID 和顶点索引的顶点代理类,因此三角形可以像操作顶点代理对象一样它以前会有顶点对象。您并没有完全消除更紧密的耦合,而是将更紧密耦合的“知识”隔离到单个类中,该类负责保持松散耦合的外观。

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

于 2010-09-06T17:00:26.850 回答
1

如果您只是在顶点列表的末尾添加或删除,请使用 adeque代替。

于 2010-09-06T17:48:02.843 回答
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。

于 2010-09-06T19:20:33.700 回答
0

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

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

于 2010-09-06T16:15:58.553 回答