我知道,除非另有說明,接受右值引用參數的所有標準庫函數都保證以有效但未指定的狀態離開移動的參數,並且某些這裏的例子可能會出現未定義的行爲,但潛在的問題並不依賴於此。字符串類成員的移動構造函數的行爲
下面的程序:
// testmove1.cpp
#include <iostream>
#include <string>
int main() {
std::string s1{"String"};
std::cout << "s1: [" << s1 << "]" << std::endl;
std::string s2{std::move(s1)};
std::cout << "s2: [" << s2 << "]" << std::endl;
std::cout << "s1 after move: [" << s1 << "]" << std::endl; // Undefined
return 0;
}
輸出:
[email protected]:~$ ./testmove1
s1: [String]
s2: [String]
s1 after move: []
[email protected]:~$
1.4.3 s1
此舉似乎未定義給我後,但被空字符串是至少一個可行的選擇。 Valgrind報告爲這個計劃做了一個單獨的分配,這是你所期望的。
如果我做一些非常相似,但有一類成員,我得到不同的結果:
// testmove2.cpp
#include <iostream>
#include <string>
class MyClass {
std::string m_data;
public:
MyClass(const std::string& data) :
m_data(data) {}
MyClass(const MyClass&& other) :
m_data{std::move(other.m_data)} {};
const std::string& get_data() const { return m_data; }
};
int main() {
MyClass c1{"Object"};
std::cout << "c1: [" << c1.get_data() << "]" << std::endl;
MyClass c2{std::move(c1)};
std::cout << "c2: [" << c2.get_data() << "]" << std::endl;
std::cout << "c1 after move: [" << c1.get_data() << "]" << std::endl;
return 0;
}
與輸出:
[email protected]:~$ ./testmove2
c1: [Object]
c2: [Object]
c1 after move: [Object]
[email protected]:~$
不具有第二串清除出去也似乎像一個可行的選擇,所以這本身並不令人驚訝。但令我驚訝的是,這種行爲與純粹將字符串放入類中的結果完全不同。 Valgrind也報告了在這裏進行的單一分配。
爲了測試他們是否實際上是指向同一個東西,搬家後,我可以改變c2
,並檢查是否c1
也得到改變:
// testmove3.cpp
#include <iostream>
#include <string>
class MyClass {
std::string m_data;
public:
MyClass(const std::string& data) :
m_data(data) {}
MyClass(const MyClass&& other) :
m_data{std::move(other.m_data)} {};
const std::string& get_data() const { return m_data; }
void change_data() { m_data[0] = 'A'; }
};
int main() {
MyClass c1{"Object"};
std::cout << "c1: [" << c1.get_data() << "]" << std::endl;
MyClass c2{std::move(c1)};
std::cout << "c2: [" << c2.get_data() << "]" << std::endl;
std::cout << "c1 after move: [" << c1.get_data() << "]" << std::endl;
c2.change_data();
std::cout << "c1 after change: [" << c1.get_data() << "]" << std::endl;
std::cout << "c2 after change: [" << c2.get_data() << "]" << std::endl;
return 0;
}
,輸出:
[email protected]:~$ ./testmove3
c1: [Object]
c2: [Object]
c1 after move: [Object]
c1 after change: [Object]
c2 after change: [Abject]
[email protected]:~$
在這裏,這兩個對象顯然不是指向相同的東西,因爲更改c2
不會影響存儲在c1
中的內容。 Valgrind現在報告了2個分配,這對於解釋所觀察到的行爲顯然是必要的,因爲我們顯然有兩個不同的字符串,但對於我來說,爲什麼突然之間因爲改變其中的一個而忽略了2個分配。如果我完全擺脫了這一舉措,只需創建c1
,然後撥打change_data()
即可,因此我只會按照您的預期獲得1次分配。
我們可以擺脫未定義行爲(不包括在我的一部分的任何其他錯誤)通過移除後移的所有訪問在c1
:
// testmove4.cpp
#include <iostream>
#include <string>
class MyClass {
std::string m_data;
public:
MyClass(const std::string& data) :
m_data(data) {}
MyClass(const MyClass&& other) :
m_data{std::move(other.m_data)} {};
const std::string& get_data() const { return m_data; }
void change_data() { m_data[0] = 'A'; }
};
int main() {
MyClass c1{"Object"};
std::cout << "c1: [" << c1.get_data() << "]" << std::endl;
MyClass c2{std::move(c1)};
std::cout << "c2: [" << c2.get_data() << "]" << std::endl;
c2.change_data();
std::cout << "c2 after change: [" << c2.get_data() << "]" << std::endl;
return 0;
}
,輸出:
[email protected]:~$ ./testmove4
c1: [Object]
c2: [Object]
c2 after change: [Abject]
[email protected]:~$
我們可以顯然不再看到c1
沒有改變,因爲我們沒有輸出它。但Valgrind仍然顯示2個分配。
任何人都知道發生了什麼,在這裏?
爲什麼一個
std::string
似乎歸零以下的舉動時,它的本身,而不是在它的類成員?在最後一個例子中,當我移動一個對象然後改變它時,爲什麼我得到兩個分配而不是一個,當我移動對象時只有一個分配,然後不改變它?我知道我們似乎正朝着量子計算的方向發展,但在C++中使用不確定性原理似乎還爲時過早。
我用G ++ 4.7.2,但我得到譁 - 503.0.40同樣觀察到的行爲。
編輯:思前想它,如果你最終在有效狀態的兩個對象,這有一定道理他們都擁有分配。這只是編譯器在確定其中一個永遠不會被使用的時候優化掉其中一個分配嗎?如果是的話,構建一個最小的例子是一個令人討厭的危險。
如果某事物處於有效狀態,那麼檢查它就不是未定義的行爲。也許你的意思是*未指定*? –
@MattMcNabb:你可能是對的,只需將它發送到'std :: cout'可能不涉及任何具有前提條件的函數。我的意思是沒有定義,因爲像'm_data [0]'這樣的移動後會有資格,但是這裏發生的事情可能不屬於這個類別。在任何情況下,這個問題都沒有密切關係。 –