为什么由value返回的未命名对象在调用其转换操作符之前被析构
Why does un-named object returned by value get destructed before its conversion operator is called?
我有一个按值返回对象的函数。接收方变量需要调用该对象上的向外转换操作符。如果在返回语句(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 Refn";
return retVal;
}
Ref bar() {
int * temp = new int(5);
std::cout << "Return anonymous Refn";
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 to int*转换。
这显然是因为MSVC在调试模式下不执行RVO,所以"复制构造函数";(Ref(Ref&)
)被调用从foo
函数返回,这被执行:
Ref(Ref & ref) : iptr(ref) {
// ...
}
其中int*
类型的iptr
是用ref
的隐式转换初始化的。
@bogdan让我注意到,这个"复制构造函数"的确有一个move构造函数语义,你可能应该为它写一个特定的Ref(Ref&&)
。
在bar
中,我们知道你正在构建一个右值return Ref(temp)
,它不能绑定到Ref(Ref&)
构造函数的左值引用,因此选择另一个构造函数并复制指针(不重置临时构造函数)。
当临时指针超出作用域时,指针是 delete
d,当从bar
构造的对象也超出作用域时,相同的指针被删除,导致未定义的行为(这是崩溃的原因)。
用c++编写该类的方法
你们班还有很多其他问题。首先,它可能导致各种崩溃,而且通常不安全。在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
。
相关文章:
- 我可以将C 17 Capture lambda ConstexPR转换操作符的结果用作函数指针模板非类型参数吗?
- Clang vs GCC vs MSVC模板转换操作符-哪个编译器是正确的
- 为什么操作符的新函数需要强制转换
- c++中从整数到字符串的转换中,左移操作符是做什么的?
- 模板类型转换操作符=
- 链接隐式转换操作符
- 强制转换操作符函数在g++中可以很好地编译,但在其他编译器中不行.为什么
- 当目标类有多个构造函数时,消除强制转换操作符的歧义
- 删除转换操作符
- 强制转换操作符重载和引用
- 实现没有临时的转换操作符
- 不同的强制转换操作符被不同的编译器调用
- 将操作符转换为模板实参的特化
- 为什么模板化的用户定义转换操作符能够确定其返回类型?
- const转换操作符
- 在可强制转换为另一个模板类型的类模板中重载赋值操作符
- 调用转换操作符不能用于静态const变量
- 内部编译器错误-开关表达式中的模板化转换操作符
- 强制转换操作符可以是显式的吗?
- 在返回语句中统一初始化,并显式将操作符转换为bool