Пример:
class A
{
....
A func();
};
A A::func()
{
...
return something;
}
На уровне сборки при компиляции функция A::func
будет фактически иметь два параметра: первый - this
указатель, а второй - адрес временного объекта A
, созданного вызывающим пользователем для хранения возвращаемого значения.
Например, если мы пишем a.func()
, программа создаст объект temp A
(пусть вызовет его t
) в стеке и передаст адрес a
как первый параметр, адрес t
качестве второго параметра, наконец, вызовет функция func
.
Вот мой вопрос: при реализации A::func
мы можем получить адрес a
- это указатель на this
; но есть ли у нас способ получить адрес t
? Как его зовут?
Будет полезно иметь его, если, например, я хотел бы сделать некоторое выделение памяти/бесплатно, прежде чем возвращать результат.
Вот пример того, что я хочу сделать:
class A
{
int * data;
A func();
};
A A::func()
{
// here "ret_p" is the pointer to the return value (let pretend that it exists)
ret_p->data = new int[some length];
...
return * ret_p;
}
Конечно, я могу создать локальный объект в A::func
а затем вернуть его; но тогда программа выполнит копию между моим локальным объектом и временным объектом, созданным вызывающим. Поскольку вызывающий объект уже создал временный объект, я надеюсь, что я смогу сэкономить время и пространство, просто используя его напрямую. Это возможно?
Ну, это возможно из c++, но я все еще надеюсь...
Поставка &a
для func
- это форма копирования.
Нет никакой гарантии, что это произойдет вообще, и нет доступа C++ к реализации.
Я не думаю, что вы в своей голове ясно, когда вы говорите: "Я хочу сделать выделение или освобождение памяти перед возвратом результата", можете ли вы привести конкретный пример? Вероятно, все, что вам нужно, стало возможным благодаря копированию elision, операторам перемещения и RAII.
Отвечая на ваш пример, вот как вы должны это делать.
class A
{
std :: vector <int> data;
A func();
};
A A::func()
{
A ret;
ret .data .assign (some_length, 123); // or whatever;
return ret;
}
Вероятно, это будет оптимизировать то, что вы хотите автоматически из-за копирования. Если вы считаете, что компилятор не будет копировать копию, добавьте конструктор перемещения.
A :: A (A && old)
: data (std :: move (old .data))
{
}
std::vector
move constructor просто скопирует указатель. Если вы хотите знать, как это работает, ну вот и доморощенный эквивалент.
class A
{
int * data;
// Allocate
A (size_t size) : data (new int [size]) {}
// Free, only if we haven't moved.
~ A () {if (data) delete [] data;}
// Move
A (A && old) : data (old .data) {old .data = nullptr;}
A (const A &) = delete; // or implement with a new allocation.
}
A & func()
работает нормально, вызывающий объект больше не создает временный объект, теперь ответственность за вызываемый объект. И когда мы оптимизируем, мы получаем правильное копирование, как и ожидалось.
В стеке нет такого параметра параметра temp A
(t
).
Если вы вызываете A a1 = a.func();
и return something;
внутри будет вызываться copy constructor
, эквивалентный этому A a1(something);
, a1
и something
разные.
Если вы A a1; a1 = a.func();
A a1; a1 = a.func();
и return something;
внутри, a1 = something;//(operator =)
a1 = something;//(operator =)
будет вызываться. a1
и something
разные.
Если вы вызываете A a1 = a.func();
и return A(p1);
внутри это эквивалентно A a1(p1);
, существует один экземпляр a1
.
Если вы вызываете a.func();
напрямую, не присваивая var и не return something;
внутри, ничего не происходит при возврате.
Если вы вызываете a.func();
непосредственно без присвоения var и return A(p1);
внутри, объект temp будет построен, а затем немедленно уничтожен.
Если вы A a1; a1 = a.func();
A a1; a1 = a.func();
и return A(p1);
внутри, будет создан объект temp, а затем operator = будет называться a1 = temp object;
и тогда объект temp будет уничтожен.
Для справки.
В конце концов, A a1 = a.func()
или A a1; a1 = a.func()
A a1; a1 = a.func()
, return something;//a var
return something;//a var
или return A(p1);//call constructor
return A(p1);//call constructor
вызовет разное поведение, я думаю, что вы можете правильно управлять памятью.
A
в стеке и использует его для хранения возвращаемого значения - по крайней мере, в режиме «отладки». В режиме «релиз» этот шаг иногда пропускается из-за оптимизации, но для сложных кодов временный объект все еще существует в стеке. Вы другие описания верны.