2017-10-19 66 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

相关问题