[英]RValue references, pointers, and copy constructors
考慮以下代碼:
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
而不直接訪問此指針並進行復制嗎?
在這條線
void bar(T&& t) { (*ptr) = t; } // <--- Unsafe!
您可以取消引用未初始化的指針。 這是未定義的行為。 您必須先調用其他兩個版本的bar之一,因為您需要為對象創建內存。
所以我會做ptr = new T(std::move(t));
。
如果您的T類型支持移動,則將調用move構造函數。
更新資料
我建議這樣。 不知道是否需要在foo中使用指針類型:
template <typename T>
class Foo {
private:
T obj;
public:
void bar(T& t) { obj = t; } // assignment
void bar(const T& t) { obj = t; } // assignment
void bar(T&& t) { obj = std::move(t); } // move assign
};
這樣可以避免內存泄漏,這對於您的方法也很容易。
如果您確實需要類foo中的指針,該怎么做:
template <typename T>
class Foo {
private:
T* ptr;
public:
Foo():ptr(nullptr){}
~Foo(){delete ptr;}
void bar(T& t) {
if(ptr)
(*ptr) = t;
else
ptr = new T(t);
}
void bar(const T& t) {
if(ptr)
(*ptr) = t;
else
ptr = new T(t);
}
void bar(T&& t) {
if(ptr)
(*ptr) = std::move(t);
else
ptr = new T(std::move(t));
}
};
假設您只調用了foo.bar(three());
沒有其他兩個調用:
您為什么認為這行得通? 您的代碼本質上與此等效:
int * p;
*p = 3;
這是未定義的行為,因為p
沒有指向類型為int
的有效變量。
沒有理由使該代碼失敗。 ptr
將指向由先前對bar
調用創建的現有int
對象,而第三個重載只會將新值分配給該對象。
但是,如果您改為這樣做:
int main() {
Foo<int> foo;
int a = 3;
const int b = 3;
foo.bar(three()); // <--- UB
return 0;
}
那個foo.bar(three());
行將具有未定義的行為(這並不意味着任何異常),因為ptr
將不是指向int
對象的有效指針。
這里的“不安全”之處在於,在為ptr分配新對象之前,您應該擔心ptr實際指向的對象的命運。
foo.bar(three());
這是不安全的,因為您必須在調用它之前授予它ptr實際上指向的東西。 在您的情況下,它指向由foo.bar(b);
創建的foo.bar(b);
但是foobar(b)
使ptr
指向一個新對象,而忘記了foobar(a)
創建的對象
可以使用更合適的代碼
template<class T>
class Foo
{
T* p;
public:
Foo() :p() {}
~Foo() { delete p; }
void bar(T& t) { delete p; ptr = new T(t); }
void bar(const T& t) { delete p; ptr = new T(t); }
void bar(T&& t)
{
if(!ptr) ptr = new T(std::move(t));
else (*ptr) = std::move(t);
}
}
;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.