Указатели и ссылки на выделенный стеком объект

Предположим, у меня есть следующий код (он может быть упрощен, но я думаю, что он будет достаточно):

struct BigObject {
   A a;
   B b;
};

struct A { ... };

struct B {
    A const* a_ptr;
    MyOtherClass someMethodUsingA() { ... }
}

class BigObjectBuilder {
    BigObject build() {
        BigObject o;
        o.a = buildA();
        o.b.a_ptr = &(o.a);

        return o;
    }
}

Проблема в том, что &(oa) может указывать на что-либо позже, потому что адрес o мог измениться, когда он был возвращен (вызывается либо перемещение, либо конструктор копирования). Теперь я обнаружил, что большую часть времени &(oa) одинаковы, поэтому вызов *(oba_ptr) не приведет к segfault. (Я думаю, что это связано с RVO, потому что тогда o не перемещается). В любом случае, этот код неверен, если я не уверен, что адрес o не изменился.

Очевидным решением было бы запросить динамическое распределение моего BigObject:

auto o_ptr = make_unique<BigObject>();

Это решение не так уж плохо (без утечек и решает предыдущую проблему.), Однако я все еще нашел его не элегантным: мне НЕ НУЖНО динамическое распределение, мне нужен только фиксированный адрес для моего BigObject. Это был бы Java-способ делать вещи, которые я думаю. Другим решением было бы использовать копию в B, но тогда все изменения в oa не повлияют на oba, и я этого не хочу.

Третьим решением будет отсутствие копии, ptr или ссылок на A в B и передача A в аргументе метода B::someMethodUsingA(). Но опять же это может быть утомительно, чтобы найти, какой аргумент передать при вызове этой функции. Иногда для класса B более естественно иметь указатель на класс A.

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

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

Есть ли способ обеспечить какой-то RVO? Я имею в виду, обеспечивается стандартом, а не компилятором. Что-то вроде предупреждения компилятора о том, что "BigObject" нельзя переместить на другой адрес "

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

Любая доля того, что работает для вас, приветствуется!

2 ответа

Решение

Я не могу понять, почему вам нужен объект структуры и его указатель в структуре BigObject, Вы можете спроектировать структуру так:

struct A { ... };
struct BigObject {
   A a;
   MyOtherClass someMethodUsingA() { /* Here you can use the variable a. */ }
};

Если вы все еще хотите кодировать по-своему, вы можете добавить функцию copy-construct и assign-construct для struct BigObject, как это:

struct BigObject 
{
     A a;
     B b;

     // copy-construct function
     BigObject(const BigObject& old) 
     {
         a = old.a;
         b = old.b;
         b.a_ptr = &a; // pay attetion
     }  

     // assign construct function
     BigObject& operator=(const BigObject& old)
     {
          if (this == &old) // avoid self-assignment
          {
              return *this;
          }

          a = old.a;
          b = old.b;
          b.a_ptr = &a; 

          return *this;
     }
};

Добавьте параметр ссылки A к функциям B, и когда BigObject вызывает функции, он может передавать свои A. A и B должны быть частными. При необходимости добавьте функции-оболочки в BigObject, которые перенаправляют на вызовы A и B, передавая параметр A по мере необходимости.

Не пишите код, который требует RVO для работы.

Другие вопросы по тегам