Когда занятия хотят, чтобы пара - PullRequest
6 голосов
/ 06 сентября 2010

У меня проблема с двумя классами, которые когда-то были хорошо разделены, но теперь они хотят соединиться.

Не вдаваясь в подробности проблемы, вот она:

Раньше у меня был класс Triangle, который содержал 3 вершины в пространстве.

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

В программе было много экземпляров Треугольника, поэтому у каждого была своя копия своих вершин. Функции-члены, такие как 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 перемещается, все треугольники, которые используют эту вершину, обновляются автоматически).

Это было бы действительно хорошим решением! Но это не сработало надежно, , потому что std :: vector делает недействительными указатели , и я использовал std :: vector для списка главных вершин в классе Scene.

Поэтому мне пришлось использовать целые числа:

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

Но теперь у меня появилась эта новая проблема связи: чтобы найти свою собственную область или центроид, классу Triangle нужен доступ к class Scene там, где раньше его не было. Похоже, что я что-то заподозрил, но не совсем.

WWYD

Ответы [ 7 ]

4 голосов
/ 06 сентября 2010

Вы можете передать вектор треугольнику в его конструкторе, чтобы он мог сохранить ссылку на вектор.Тогда ему не нужно получать доступ или знать о сцене.

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)
        {}
};
4 голосов
/ 06 сентября 2010

Почему бы просто не vector in Scene просто хранить указатели тоже?

std::vector<Vertex *> masterVertexList;

Таким образом, Triangle может продолжать использовать Vertex *, и все, что вам нужно сделать, это убедиться, что указатели удалены в деструкторе Scene.

3 голосов
/ 06 сентября 2010

Мне кажется, что ваш Треугольник действительно зависит от вашей Сцены (поскольку все его вершины являются членами этой конкретной сцены), поэтому нет ничего постыдного в том, чтобы заставить объект сделать это. На самом деле, я бы, вероятно, дал Треугольнику обязательный элемент Scene *.

1 голос
/ 06 сентября 2010

Я не думаю, что это слишком плохо. 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 и т. Д., Что можно сделать с помощью поиска и замены, и в любом случае улучшает ОО стиль.

Или покончи с конструктором и private и сделайте его POD.

1 голос
/ 06 сентября 2010

Если вы только добавляете или удаляете концы списка вершин, используйте deque.

1 голос
/ 06 сентября 2010

Переход от несвязанного к связанному является естественным результатом вашего решения обмениваться вершинами, где это возможно. Ранее каждый треугольник «владел» своими вершинами, а сцена (предположительно) владела связкой или треугольниками.

Разрешение треугольникам совместно использовать вершины изменяет эту фундаментальную модель - когда / если вершина может быть разделена между двумя или более треугольниками, ни один треугольник больше не может владеть этой вершиной. Хотя возможно (например, с чем-то вроде shared_ptr) иметь распределенную схему совместного владения, то, что вы делаете сейчас, вероятно, более просто: у каждой вершины все еще есть один владелец, но владелец теперь сцена вместо отдельного треугольника.

Поскольку треугольник теперь является лишь удобным способом группировки некоторых вершин в «владеющей» коллекции вместо владения самими вершинами, неудивительно, что существует тесная связь между треугольником и коллекцией, которая владеет его вершинами. Если вас это сильно волнует, вы все равно можете скрыть общее владение, чтобы сохранить хотя бы внешний вид вашей предыдущей более слабой связи.

Общая идея была бы довольно проста: вместо того, чтобы каждый треугольник знал сцену, содержащую вершины треугольника, вы бы создали прокси-класс вершины, который комбинирует идентификатор сцены и индекс вершины, поэтому треугольник может манипулировать прокси вершины. Объект, как и прежде, будет иметь объект вершины. Вы не полностью устраняете более плотное сцепление, но вы изолируете «знание» о более плотном соединении для одного класса, это только , ответственный за поддержание внешнего вида более слабого сцепления.

Очевидным недостатком этого является то, что объекты вершинного прокси, скорее всего, будут хранить достаточное количество избыточных данных. Например, все прокси вершин в любом конкретном треугольнике четко представляют вершины в одной сцене. Если вы храните идентификатор сцены явно для каждого вершинного прокси, вы сохраняете три копии идентификатора сцены вместо той, которая была у вас ранее. Иногда это стоит - другие это не так. Если это реальная проблема, вы можете попытаться придумать способ избежать явного хранения идентификатора сцены, но, вероятно, это будет связано с некоторыми трюками, которые не являются (даже близкими) к языковой независимости.

0 голосов
/ 06 сентября 2010

Если у вас есть только один Scene, вы можете сделать его одноэлементным объектом и получать доступ к списку вершин статическими методами.

Если у вас есть несколько Scene объектов, то каждый Triangle принадлежит ровно одному Scene - и он должен «знать», к какой сцене он принадлежит. Поэтому вы должны инициализировать каждый треугольник ссылкой Scene и сохранить его как член класса.

...