У меня есть функция, которая возвращает объект по значению. Переменная получателя требует вызова оператора внешнего преобразования для этого объекта.
Если я создаю возвращенный объект в операторе возврата (RVO), его деструктор вызывается перед оператором внешнего преобразования.
Однако, если я назову объект и верну его, оператор внешнего преобразования вызывается до разрушения объекта.
Это почему?
#include <iostream>
class Ref {
public:
Ref(int * ptr) : iptr(ptr) {
std::cout << "Ref Constructed at: " << long(this) << " Pointing to: " << long(ptr) << '\n';
}
Ref(Ref & ref) : iptr(ref) {
std::cout << "Ref Moved to: " << long(this) << '\n';
ref.iptr = nullptr;
}
operator int () {
std::cout << "Ref-To int: Temp at: " << long(iptr) << '\n';
return *iptr;
}
operator int* () {
std::cout << "Ref-To int*: Temp at: " << long(iptr) << '\n';
return iptr;
}
~Ref() {
delete iptr;
std::cout << "Ref at: " << long(this) << " Deleted: " << long(iptr) << '\n';
}
private:
int * iptr;
};
Ref foo() {
int * temp = new int(5);
Ref retVal(temp);
std::cout << "Return named Ref\n";
return retVal;
}
Ref bar() {
int * temp = new int(5);
std::cout << "Return anonymous Ref\n";
return Ref(temp);
}int _tmain(int argc, _TCHAR* argv[])
{
std::cout << "********* Call foo() *************\n";
int result = foo();
std::cout << "\n********* Call bar() *************\n";
int result2 = bar();
return 0;
}
Выход из этого:
********* Call foo() *************
Ref Constructed at: 2356880 Pointing to: 5470024
Return named Ref
Ref-To int*: Temp at: 5470024
Ref Moved to: 2356956
Ref at: 2356880 Deleted: 0
Ref-To int: Temp at: 5470024
Ref at: 2356956 Deleted: 5470024
********* Call bar() *************
Return anonymous Ref
Ref Constructed at: 2356680 Pointing to: 5470024
Ref-To int*: Temp at: 5470024
Ref Constructed at: 2356968 Pointing to: 5470024
Ref at: 2356680 Deleted: 5470024
Ref-To int: Temp at: 5470024
Press any key to continue . . .
Когда вызывается bar (), ссылка удаляется перед вызовом оператора преобразования, и происходит сбой.
Кроме того, я не понимаю, почему преобразование Ref в int * вызывается при построении возвращаемого значения.
Я не понимаю, почему преобразование Ref в int * вызывается при построении возвращаемого значения.
Это происходит потому, что, по-видимому, MSVC не выполняет RVO в режиме отладки, поэтому «конструктор копирования» (Ref(Ref&)
) вызывается, чтобы вернуться из foo
функция, и это выполняется:
Ref(Ref & ref) : iptr(ref) {
// ...
}
где iptr
типа int*
, инициализируется неявным преобразованием из ref
,
Как @bogdan заставил меня заметить, что этот ваш «конструктор копирования» действительно имеет семантический конструктор перемещения, и вы, вероятно, должны написать Ref(Ref&&)
вместо этого.
В bar
У нас есть, что вы строите Rvalue return Ref(temp)
который не может быть привязан к ссылке на lvalue для Ref(Ref&)
конструктор, и, следовательно, другой конструктор выбирается и указатель копируется в (без сброса временного).
Когда временный выходит из области видимости, указатель delete
г, и когда построенный объект из bar
также выходит из области видимости, тот же указатель удаляется, вызывая неопределенное поведение (что является причиной вашего сбоя).
У вашего класса много других проблем. С одной стороны, это может привести к различным сбоям и, как правило, не безопасно для памяти. В C ++ вы могли бы написать что-то вроде этого для этого класса:
class Ref {
public:
explicit Ref(std::unique_ptr<int> ptr)
: iptr(std::move(ptr))
{}
int get() const { return *iptr; }
int* data() const { return iptr.get(); }
private:
std::unique_ptr<int> iptr;
};
или даже просто std::unique_ptr
,
Дело в том, что неявные преобразования и ручное динамическое распределение памяти часто приводят ко многим ошибкам и «сбоям». Избегайте их как чумы в максимально возможной степени.