我一直在編寫自己的String類,但我不確定如何正確地編寫運算符+,考慮到可以將rvalue傳遞給它。我想我應該有以下3個非成員函數使用右值編寫運算符+的正確方法
String operator+(String &&lhs, String &&rhs);
String operator+(String& lhs,String&&rhs);
String operator+(String&&lhs,String&rhs);
但是我不知道如何實現它們。任何幫助,將不勝感激。
我一直在編寫自己的String類,但我不確定如何正確地編寫運算符+,考慮到可以將rvalue傳遞給它。我想我應該有以下3個非成員函數使用右值編寫運算符+的正確方法
String operator+(String &&lhs, String &&rhs);
String operator+(String& lhs,String&&rhs);
String operator+(String&&lhs,String&rhs);
但是我不知道如何實現它們。任何幫助,將不勝感激。
首先,確保你的String
類中定義複製和移動構造函數:
class String
{
private:
char *m_data;
std::size_t m_length;
...
public:
String();
String(const String &src);
String(String &&src);
~String();
...
};
String::String() :
m_data(nullptr),
m_length(0)
{
}
String(const String &src) :
m_data(new char[src.m_length+1]),
m_length(src.m_length)
{
std::copy_n(src.m_data, m_length, m_data);
m_data[m_length] = 0;
}
String(String &&src) :
m_data(nullptr),
m_length(0)
{
std::swap(m_data, src.m_data);
std::swap(m_length, src.m_length);
}
String::~String()
{
delete[] m_data;
}
然後定義operator+
和operator+=
的類:
class String
{
public:
...
String& operator+=(const String &rhs);
...
friend String operator+(String lhs, const String &rhs)
{
lhs += rhs;
return lhs;
}
};
String& String::operator+=(const String &rhs)
{
String tmp;
tmp.m_length = m_length + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(m_data, m_length, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + m_length);
tmp.m_data[tmp.m_length] = 0;
std::swap(m_data, tmp.m_data);
std::swap(m_length, tmp.m_length);
return *this;
}
通過以const String &
作爲右側的輸入,它將處理左值和右值輸入。
對於operator+
,左邊是按值取值,因此編譯器可以根據輸入是左值(複製)還是右值(移動)來決定使用哪個最佳構造函數。
或者,你可以實現它採取const String &
左側所以它仍然處理左值和右值,但你必須實現它類似於如何operator+=
實現以避免連接到它之前複製lhs
的額外分配:
friend String operator+(const String &lhs, const String &rhs)
{
/*
String tmp(lhs);
tmp += rhs;
return tmp;
*/
String tmp;
tmp.m_length = lhs.m_length + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(lhs.m_data, lhs.m_length, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + lhs.m_length);
tmp.m_data[tmp.m_length] = 0;
return tmp;
}
無論哪種方式,你也應該定義const char *
輸入轉換的構造和operator+
還有:
class String
{
public:
...
String(const char *src);
...
friend String operator+(const char *lhs, const String &rhs)
{
return String(lhs) + rhs;
/* or:
std::size_t len = std::strlen(lhs);
String tmp;
tmp.m_length = len + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(lhs, len, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + len);
tmp.m_data[tmp.m_length] = 0;
return tmp;
*/
}
...
};
String::String(const char *src) :
m_data(nullptr),
m_length(std::strlen(src))
{
m_data = new char[m_length+1];
std::copy_n(src, m_length, m_data);
m_data[m_length] = 0;
}
這將允許級聯String
對象與字符串(String + "literal"
,"literal" + String
,String += "literal"
等)。
有關更多詳細信息,請參閱operator overloading和cppreference.com。
這些函數的版本在左側採用左值引用可能是有價值的,因爲那時臨時可以被用'+'創建的新值進行拆分。但是對於String來說,這是一個值得懷疑的微觀優化(假定「String」意味着一個字符向量並且意味着連接)。 – Omnifarious
@Omnifarious:已更新 –
是的,比左側的右值引用好得多。 – Omnifarious
的方式,我通常做的是這樣的:
class foo
{
...
public:
...
foo&& operator +(foo const & other) &&;
foo&& operator +(foo && other) const &;
foo&& operator +(foo && other) &&;
foo operator +(foo const & other) const &;
};
不知道微軟支持這個,但是這是在最近的標準來做到這一點的好辦法。嘗試鏗鏘如果msvc不會讓你。
這樣做的好處是您可以對使用的方法進行很好的控制。如果需要,這4個操作也可以在班級之外定義。但是,對於r值/ l值組合的4種可能性,您總是需要4。
此外,您通常希望將l值限定爲const以指示它們未被修改。
簡單地定義複製/移動構造函數通常不是解決此問題的有效方法。您需要很好地理解右值引用如何有效地實現這一點。
我可能會補充說,其他答案將工作,但效率不高。它實際上並不實際處理右值引用,而是將它們視爲左值常量引用。這將導致效率低下但可用的代碼。 – Exaeta
你絕對缺少標準重載'const String&,const String&'。你可以在文檔中找到一些指導和例子(http://en.cppreference.com/w/cpp/language/operators)。 – nwp
在你編寫自己的字符串類之前有數百萬人,並且從來沒有做過任何比標準字符串類更好或更少的錯誤。所以,除非這只是一項學術活動,否則放棄目標! –
@ChristopherPisz如果'std :: string'的作者有這樣的行爲,我們將只有最糟糕的可用實現。 – user463035818