2017-10-19 48 views
0

我一直在編寫自己的String類,但我不確定如何正確地編寫運算符+,考慮到可以將rvalue傳遞給它。我想我應該有以下3個非成員函數使用右值編寫運算符+的正確方法

String operator+(String &&lhs, String &&rhs); 
String operator+(String& lhs,String&&rhs); 
String operator+(String&&lhs,String&rhs); 

但是我不知道如何實現它們。任何幫助,將不勝感激。

+0

你絕對缺少標準重載'const String&,const String&'。你可以在文檔中找到一些指導和例子(http://en.cppreference.com/w/cpp/language/operators)。 – nwp

+0

在你編寫自己的字符串類之前有數百萬人,並且從來沒有做過任何比標準字符串類更好或更少的錯誤。所以,除非這只是一項學術活動,否則放棄目標! –

+0

@ChristopherPisz如果'std :: string'的作者有這樣的行爲,我們將只有最糟糕的可用實現。 – user463035818

回答

2

首先,確保你的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" + StringString += "literal"等)。

有關更多詳細信息,請參閱operator overloadingcppreference.com

+0

這些函數的版本在左側採用左值引用可能是有價值的,因爲那時臨時可以被用'+'創建的新值進行拆分。但是對於String來說,這是一個值得懷疑的微觀優化(假定「String」意味着一個字符向量並且意味着連接)。 – Omnifarious

+1

@Omnifarious:已更新 –

+0

是的,比左側的右值引用好得多。 – Omnifarious

1

的方式,我通常做的是這樣的:

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以指示它們未被修改。

簡單地定義複製/移動構造函數通常不是解決此問題的有效方法。您需要很好地理解右值引用如何有效地實現這一點。

+0

我可能會補充說,其他答案將工作,但效率不高。它實際上並不實際處理右值引用,而是將它們視爲左值常量引用。這將導致效率低下但可用的代碼。 – Exaeta

相關問題