Взаимные ссылки на классы в C ++ - PullRequest
0 голосов
/ 02 декабря 2018

В моей компании есть код, который принимает следующую форму:

class ObjectA {
public:
  ObjectA(ObjectB &objectB): objectB(objectB) { }

void Initialize() { ... }

private:
  ObjectB &objectB;
};

class ObjectB {
public:
  ObjectB(ObjectA &objectA): objectA(objectA) { }

void Initialize() { ... }

private:
  ObjectA &objectA;
};

По сути, два класса имеют взаимозависимость друг от друга.

Само по себе это не то, что беспокоитменя, (хотя это не лучший дизайн IMO) , это то, что взаимные зависимости передаются через конструктор по ссылке в обеих ситуациях .

Итак, единственный способфактически построить эту пару объектов (они оба требуются в приложении) означает передать неполную ссылку в качестве зависимости.Чтобы учесть это, отдельные Initialize методы добавляются как конструктор 2-го этапа, который затем вызывается после создания обоих объектов.Следовательно, конструкторы часто ничего не делают, кроме как присваивают параметры конструктора внутренним объектам и инициализируют все в методе * 1015. *

Хотя код может быть действительным, я склоняюсь к тому, что это в корне невернодизайн по следующим причинам:

  1. Состояние зависимого объекта не может быть гарантировано в конструкторе
  2. Требуется, чтобы разработчики сделали дополнительный вызов к Initialize
  3. Это исключает использование предупреждений компилятора, которые проверяют, были ли инициализированы переменные-члены
  4. Ничто не мешает многократному вызову Initialize, что может привести к странным, трудно отслеживаемым ошибкам

Мои коллеги говорят мне, что наличие отдельного метода инициализации упрощает конструирование объекта, поскольку разработчику не нужно беспокоиться о том, какие объекты заказа объявлены в корне приложения.Они могут быть объявлены и построены в совершенно произвольном порядке, поскольку все гарантированно будет действительным после вызова Initialize.

Я выступал за то, чтобы конструкция объекта следовала тому же порядку графа зависимостей, и что эти объекты должныбыть переработан, чтобы устранить взаимную зависимость, но я хотел посмотреть, что гуру SO должен сказать по этому поводу.Неужели я ошибаюсь, считая, что это плохая практика?

Редактировать:

Способ, которым эти классы на самом деле создаются, - через фабричный класс, как показано ниже:

Factory {
public:
  Factory():
    objectA(objectB),
    objectB(objectA) {
  }
private:
  ObjectA objectA;
  ObjectB objectB;
};

Ответы [ 2 ]

0 голосов
/ 03 декабря 2018

Мне тоже не нравится опубликованный код - он излишне сложен и хрупок.Обычно существует два понятия владения, когда два класса взаимодействуют таким образом, поэтому, если объекты типа A имеют собственные объекты типа B (что они, вероятно, должны), тогда вы должны сделать что-то вроде:

#include <memory>

class A;

class B
{
public:
    B (A& a) : a (a) { }
private:
    A& a;
};

class A
{
public:
    A () { b = std::make_unique <B> (*this); }

private:
    std::unique_ptr <B> b;
}; 

Живая демоверсия

0 голосов
/ 02 декабря 2018

Это плохая практика, да.Передача ссылки на objectB для objectA для работы, пока она не инициализирована должным образом, является нет-нет.

Теперь это может быть стандартный и безопасный код, потому что ObjectA не пытается получить доступ к переданной ссылке на ObjectB в своем конструкторе, но эта безопасность зависает от потока.Если позже кто-то решит просто инициализировать в конструкторе или получить доступ к чему-либо из objectB, или изменить его любым другим способом, где будет использоваться objectB, вы получите UB.

Это похоже на случай использованиядля указателей, которые повторно устанавливаются после запуска обоих конструкторов.

...