背景及問題 如下程式所示: #include<iostream> class MyString { public: MyString() = default; MyString(const char* data) { printf("%s", "MyString Constructed!!\n"); ...
背景及問題
如下程式所示:#include<iostream>
class MyString {
public:
MyString() = default;
MyString(const char* data)
{
printf("%s", "MyString Constructed!!\n");
size = strlen(data);
m_data = new char[size];
memcpy(m_data, data, size);
}
~MyString()
{
if (m_data)
{
printf("%s", "MyString Destroyed!!\n");
delete m_data;
}
}
//copy constructor
MyString(const MyString& other) noexcept
{
printf("%s", "MyString Copyed!!\n");
size = other.size;
m_data = new char[size];
memcpy(m_data, other.m_data, size);
}
private:
char* m_data;
int size;
};
class Entity
{
public:
//constructor
Entity(const MyString& string):m_string(string) {}
private:
MyString m_string;
};
int main()
{
Entity entity("Hello");
return 0;
}
程式說明
程式定義了一個MyString類,其中構造函數和拷貝構造函數需要對傳進來的字元串開闢空間並複製內容,另外一個Entity類含有一個MyString成員,併在初始化時複製傳入的MyString對象。主程式Main中以常量字元串構造一個entity示例。
運行程式會發現,MyString構造了一次,拷貝一次,程式結束析構兩次,符合運行邏輯。“Hello”字元串先通過構造函數構造一個臨時變數MyString,臨時變數再通過Entity內的構造函數拷貝構造給成員變數m_string
問題在於臨時變數拷貝構造需要重新開闢空間,並且“用完即扔”,為什麼不直接將“hello”構造好的MyString直接“移動”到Entity?這樣會節省空間,提高效率。用此引出移動構造和std::move()
移動構造與std::move()
若要實現將臨時變數移動到Entity,首先MyString要加入移動構造,如下: MyString(MyString&& other) noexcept
{
printf("%s", "MyString Moved!!\n");
size = other.size;
m_data = other.m_data;
//clear origin data
other.size = 0;
other.m_data = nullptr;
}
此為移動構造,接受的是一個右值,構造是直接複製原MyString的size與data,不重新開闢空間做深拷貝。並將原MyString清零。接著對Entity構造時使用std::move通知移動構造函數,如下:
Entity(MyString&& string) :m_string(std::move(string)) {}
需要註意的是
1. Entity右值構造時也可不使用std::move,直接將參數強轉為右值類型也可以,std::move相當於通知構造函數以移動構造的方式進行Entity(MyString&& string) :m_string((MyString&&)string) {}
2. 對於形參為Const YourType &類型的既可以接受左值,也可以接受右值。但是使用std::move會編譯錯誤,因為Const值不能被移動,所以Entity構造仍要單獨寫一個右值構造
3. MyString移動構造時,複製了臨時數據的值還要對其清空,因為數據已經被移動,指針沒有置空,析構兩次會引起Crash問題 作者:robot2017
出處:https://www.cnblogs.com/stephen2023/p/17913415.html
版權:本文版權歸作者和博客園共有
轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接;否則必究法律責任