RValue 引用、指针和复制构造函数
考虑下面的代码:
int three() {
return 3;
}
template <typename T>
class Foo {
private:
T* ptr;
public:
void bar(T& t) { ptr = new T(t); }
void bar(const T& t) { ptr = new T(t); }
void bar(T&& t) { (*ptr) = t; } // <--- Unsafe!
};
int main() {
Foo<int> foo;
int a = 3;
const int b = 3;
foo.bar(a); // <--- Calls Foo::bar(T& t)
foo.bar(b); // <--- Calls Foo::bar(const T& t)
foo.bar(three()); // <--- Calls Foo::bar(T&& t); Runs fine, but only if either of the other two are called first!
return 0;
}
我的问题是,为什么第三个重载 Foo::bar(T&& t)
会使程序崩溃?这里到底发生了什么?函数返回后参数t
是否会被销毁?
此外,我们假设模板参数 T 是一个非常大的对象,具有非常昂贵的复制构造函数。有没有办法使用 RValue 引用将其分配给 Foo::ptr ,而无需直接访问此指针并进行复制?
Consider the following piece of code:
int three() {
return 3;
}
template <typename T>
class Foo {
private:
T* ptr;
public:
void bar(T& t) { ptr = new T(t); }
void bar(const T& t) { ptr = new T(t); }
void bar(T&& t) { (*ptr) = t; } // <--- Unsafe!
};
int main() {
Foo<int> foo;
int a = 3;
const int b = 3;
foo.bar(a); // <--- Calls Foo::bar(T& t)
foo.bar(b); // <--- Calls Foo::bar(const T& t)
foo.bar(three()); // <--- Calls Foo::bar(T&& t); Runs fine, but only if either of the other two are called first!
return 0;
}
My question is, why does the third overload Foo::bar(T&& t)
crash the program? What exactly is happening here? Does the parameter t
get destroyed after the function returns?
Furthermore, let's assume that the template parameter T
was a very large object with a very costly copy constructor. Is there any way to use RValue References to assign it to Foo::ptr
without directly accessing this pointer and making a copy?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在这一行
void bar(T&& t) { (*ptr) = t; } } // <--- 不安全!
您可以取消引用未初始化的指针。这是未定义的行为。
您必须首先调用 bar 的其他两个版本之一,因为您需要为对象创建内存。
所以我会这样做
ptr = new T(std::move(t));
。如果您的类型 T 支持移动,则将调用移动构造函数。
更新
我会建议类似的事情。不确定您是否需要 foo 中的指针类型:
这将避免内存泄漏,这对于您的方法来说也很容易。
如果你确实需要你的 foo 类中的指针,怎么样:
In this line
void bar(T&& t) { (*ptr) = t; } // <--- Unsafe!
you can dereference an uninitialized pointer. This is undefined behavior.
You must call one of the two other version of bar first because you need to create the memory for your object.
So I would do
ptr = new T(std::move(t));
.If your type T supports moving the move constructor will get called.
Update
I would suggest something like that. Not sure if you need the pointer type within foo:
This would avoid memory leaks which are also quite easy with your approach.
If you really need the pointer in your class foo how about that:
该代码没有理由失败。
ptr
将指向先前调用bar
创建的现有int
对象,第三次重载只会将新值分配给该对象。但是,如果您这样做:
foo.bar(two());
行将具有未定义的行为(这并不意味着任何异常),因为ptr
不会是指向int
对象的有效指针。There's no reason for that to fail in that code.
ptr
will point to an existingint
object created by the previous calls tobar
and the third overload will just assign the new value to that object.However, if you did this instead:
That
foo.bar(three());
line would have undefined behaviour (which does not imply any exception), becauseptr
would not be a valid pointer to anint
object.假设您仅调用了
foo.bar(三());
而没有其他两个调用:您为什么认为这可行?您的代码本质上与此相同:
这是未定义的行为,因为
p
没有指向int
类型的有效变量。Assuming that you only called
foo.bar(three());
without the other two calls:Why did you think that'd work? Your code is essentially equivalent to this:
That's undefined behaviour, because
p
isn't pointing to a valid variable of typeint
.“不安全”的事情是,在分配给 ptr 一个新对象之前,您应该担心 ptr 实际指向的对象的命运。
是不安全的,因为你必须在调用它之前授予 ptr 实际上指向某个东西。在你的例子中,它指向由
foo.bar(b);
创建的内容,但是
foobar(b)
使ptr
指向一个新的对象忘记了由foobar(a)
创建的对象更合适的代码可以是
;
The "unsafe"thing, here is that, before assigning to ptr a new object, you should worry about the destiny of what ptr actually points to.
is unsafe in the sense that you have to grant -before calling it- that ptr actually point to something. In your case it points to what was created by
foo.bar(b);
But
foobar(b)
makesptr
to point to a new object forgetting the one created byfoobar(a)
A more proper code can be
;