Указатели и ссылки на выделенный стеком объект
Предположим, у меня есть следующий код (он может быть упрощен, но я думаю, что он будет достаточно):
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 для работы.