2011-03-28 72 views
11

在书C++入门手册中,它有一个C型字符数组的代码,并说明如何在文章15.3运算符=中过载=运算符。现在运算符= C++中的重载

String& String::operator=(const char *sobj) 
{ 
    // sobj is the null pointer, 
    if (! sobj) { 
     _size = 0; 
     delete[] _string; 
     _string = 0; 
    } 
    else { 
     _size = strlen(sobj); 
     delete[] _string; 
     _string = new char[ _size + 1 ]; 
     strcpy(_string, sobj); 
    } 
    return *this; 
} 

我想知道为什么有需要返回参考String &时,下面这段代码做相同的工作,没有任何问题:

void String::operator=(const char *sobj) 
{ 
    // sobj is the null pointer, 
    if (! sobj) { 
     _size = 0; 
     delete[] _string; 
     _string = 0; 
    } 
    else { 
     _size = strlen(sobj); 
     delete[] _string; 
     _string = new char[ _size + 1 ]; 
     strcpy(_string, sobj); 
    } 

} 
  • 请大家帮帮忙。
+3

注意,在书中提出的解决方案是不例外安全(和这将会非常简单,我不明白他们为什么没有这样做)。 – 2011-03-28 19:15:54

回答

20

它支持以下成语:

String a, b; 
const char *c; 

// set c to something interesting 

a = b = c; 

对于这项工作,b = c必须返回一个适当的对象或参考分配给a;根据C++运算符优先规则,它实际上是a = (b = c)

如果您要返回指针this,则必须编写a = *(b = c),但不能表达意图的含义。

+1

现在,这是一个单行 - 为什么你写这本书。谢谢。得到了我的答案。 – Sadique 2011-03-28 18:38:09

+0

我必须等上9分钟才能接受这个答案!!!!!! – Sadique 2011-03-28 18:41:36

+1

@Acme:没错,你必须等到其他人都有机会证明我错了。 – 2011-03-28 18:44:18

0

从运营商返回=是,所以你可以链接的东西。看到这里不附带任何条件:

x = y = 2; 

该行的y=2一部分返回2.这是X如何得到的值。如果op =返回void,则不能链接=操作。

3

公约。内置类型做到这一点,自动生成的赋值运算符做到这一点,它可以让你做到这一点:

a = b = c; 

或本:

if (foo = come_function()) { 
    // use foo here 
} 
+0

+1作为条件赋值,我忘了那一个。简而言之,这个惯例给了你C++程序员喜欢的所有枪瞄能力;) – 2011-03-28 18:46:21

+0

请注意,后者是不好的风格,应该避免。 – 2011-03-28 18:47:25

+0

请注意,不管后者是否是不好的风格都是前面讨论过的味道问题。有时候我会习惯使用这个习语,而当我这样做时,我仍然不会感到非常尴尬。虽然我不喜欢它,但我曾经这样做过;这是一个老C程序员的习惯。 – 2011-03-28 18:49:21

0

按照惯例,你能做到如:

int x, y, z; 
x = y = z = 0; 

if ((x = 1) != 0) { ... } 

为了保持这些语义的类的实例,你需要返回*this - 允许链式分配。如果你只返回this你会指向返回到您的实例,并链接分配是行不通的 - 有没有operator=String *,有一个String

8

@larsmans已经回答了你确切的问题,所以我实际上会离题:这是一些蹩脚的代码!

这里的问题是3倍:

  1. 你刚才复制的拷贝构造函数(有点)
  2. strcpy可以更好地通过strncpy取代,后者做了一些边界检查
  3. 的代码这是不是例外安全

1)和2)更多styli stic比任何东西,但3)是一个大问题

编辑:正如@Jerry Coffin所指出的,这不能防止自我分配。这就是说,如果sobj_string指向相同的字符数组,则会遇到很大的麻烦。这篇文章最后的简单解决方案也涵盖了这种情况。

也不例外安全

让我们看一下代码,即一个部分,该部分else

_size = strlen(sobj); 
    delete[] _string; 
    _string = new char[ _size + 1 ]; 
    strcpy(_string, sobj); 

如果由于某种原因,new抛出,会发生什么?

  • _size有新的字符串的值
  • _string指向旧的指针......已经释放

因此不仅对象将处于不可用状态(半来自新对象的数据,一半来自老),但它甚至不能被破坏(除非析构函数泄漏......?)

添加异常安全,硬盘的方式

_size = strlen(sobj); 
    delete[] _string; 

    try { 
    _string = new char[ _size + 1 ]; 
    } catch(...) { 
    _size = 0; _string = 0; 
    throw; 
    } 
    strcpy(_string, sobj); 

好,它是真正的底线,但它带给我们的基本异常保证:没有功能的保证,但保证该代码是技术上是正确的(无碰撞,无泄漏)。

添加异常安全,最简单的方法:复制和交换成语

找到一个更完整的描述:What is the copy-and-swap idiom ?

void swap(String& lhs, String& rhs) { 
    using std::swap; 
    swap(lhs._size, rhs._size); 
    swap(lhs._string, rhs._string); 
} 

String& String::operator=(String other) { // pass-by-value 
    swap(*this, other); 
    return *this; 
} 

它是如何工作的?

  • 我们重用的实际拷贝
  • 我们重用交换功能的交换价值的拷贝构造函数
  • 我们重新用于清理

析构函数和它比甚至更好以前的版本,现在我们有强烈的例外保证:它是事务性的,因此如果它失败了,我们分配给的字符串是不变的(就像什么都没发生过一样)。

More about Exception Guarantees

我有点气馁,一个C++教程会促进这种可疑代码,请告诉我它是什么不该做的例子:/

+0

@Jerry:好点,我会编辑它。 – 2011-03-29 06:37:18