C ++ избегать создания объекта - PullRequest
2 голосов
/ 27 апреля 2011

У меня есть класс, все конструкторы которого по умолчанию находятся вне моего контроля.Мне нужно избегать их конструирования в одних условиях, а не в других.Есть ли что-нибудь, что я могу сделать это?

Шаблон выглядит примерно так:

class A
{
public: 
    //ctor + dtor + methods
    A(); 
    ~A();
    // Data members beyond my control
    X1 x1;
    X2 x2;
    ...
    XN xn;
};

class B
{
public:
    B()
    {}

    // Data members
    A a;   // This instance of A is the issue
};

Проблема в том, что от X1 до XN полностью вне моего контроля.Класс A - это просто класс, который упаковывает x1 в xn вместе.Я хочу условно создать экземпляр A в B.Теперь проблема в том, что конструкторы по умолчанию от X1 до XN делают что-то, чего следует избегать в одних условиях, а не в других.Поэтому независимо от того, создаю я A или нет, B будет пытаться создать конструктор по умолчанию A, который, в свою очередь, будет инициировать конструкторы по умолчанию от X1 до XN.Это то, чего мне нужно избегать.

В настоящее время я использую макросы, чтобы исправить это, но хотел посмотреть, есть ли лучшее решение.

Ответы [ 3 ]

4 голосов
/ 30 апреля 2011

Как отметил Конрад Рудольф:

class B
{
public:
  B(bool create = false)
  {
    a = (create ? new A : NULL);
  }
  ~B()
  {
    delete a;
  }
private:
  A* a;
};
1 голос
/ 30 апреля 2011

Возможно, вы захотите использовать тип Nullable, такой как boost :: необязательный.Это будет выглядеть так:

class B {
public:
    B()
    {
        /*
         * Here, a has been default constructed and is empty
         * You can do computations here and then...
         */
        if(/* some elaborate condition*/) {
            a = A();
            /* access *a from here on */
        } else {
            /* anything you want */
        }
    }

private:
    boost::optional<A> a;
};

Это ответ на вопрос, но я думаю, что более подходящий ответ мог бы быть дан, если бы вы сказали, чего вы действительно хотели достичь.Я чувствую, что это скорее проблема дизайна, чем проблема языка.Далее следуют расширенные мысли.

Что входит в предложение else в приведенном выше «решении»?Поскольку A, очевидно, может быть сконструирован только по умолчанию, это не значит, что вы можете сделать другой вызов конструктора.Однако если вы не инициализируете a, вы вводите (цикломатическую) сложность, поскольку это будет означать, что каждый метод должен будет проверять, активен ли a.В качестве альтернативы вы можете бросить;Однако я бы рефакторинг кода, который выполняет проверку (или что-то еще) в функции;либо частный статический метод, либо анонимная / статическая автономная функция, подобная этой:

namespace {
A
prepare_A()
{
    /* elaborate computations, possibly throw */
    return A();
    /*
     * if A had different constructors we could also conditionally
     * return an A(0) or whatever.
     */
}
} // namespace

B::B()
:
    a(prepare_A())
{
    /*
     * a is of type A, not boost::optional<A>
     */
}

Это, однако, предполагает, что A можно копировать.Если это не тот случай или если нельзя копировать A, я бы посчитал первое решение приемлемым при условии, что как часть инварианта класса a никогда не будет пустым.

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

class B {
public:
    explicit
    B(A& a_)
    /*
     * Important: not A const&,
     * we only want lvalues.
     * Contract on the caller: a must remain valid during
     * the lifetime of *this.
     */
    :
        a(&a_)
    {
        /*
         * class invariants: a != 0,
         * *a remains valid for the lifetime of B
         */
    }

private:
    A* a;
};

Таким образом, выне вводите цикломатическую сложность, и вам все равно, как и когда строится А.Контракт добавляет небольшую нагрузку на вызывающего, но, поскольку мы можем передавать конструктору только lvalues, злоупотреблять им очень сложно (*).

0 голосов
/ 09 декабря 2014

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

От Страуструпа, 4-е изд:

Если у объединения есть член с определяемым пользователем конструктором, операцией копирования, операцией перемещения или деструктором, то эта специальная функция удаляется (§3.3.4, §17.6.4) для этого объединения; то есть его нельзя использовать для объекта типа объединения.

Так что, если вы хотите не построить A, когда он находится в B, используйте:

class B {
public:
    B()
    {}

    // Data members
    union {
       A a;   // This instance of A is the issue
    };
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...