简体   繁体   English

分配超载:为什么使用同一对象会导致程序出现问题?

[英]Assignment overloading: why does using the same object cause problems in the program?

Suppose we have the following: 假设我们有以下内容:

class StringClass
{
public:
    ...
    void someProcessing( );
    ...
    StringClass& operator=(const StringClass& rtSide);
    ...
private:
    char *a;//Dynamic array for characters in the string
    int capacity;//size of dynamic array a
    int length;//Number of characters in a
};

StringClass& StringClass::operator=(const StringClass& rtSide)
{
    capacity = rtSide.capacity;
    length = rtSide.length;
    delete [] a;
    a = new char[capacity];

    for (int i = 0; i < length; i++)
       a[i] = rtSide.a[i];

    return *this;
}

My question is: why does this implementation of overloading the assignment operator cause problems when we try to assign an object to itself like: 我的问题是:当我们尝试向其自身分配对象时,这种重载赋值运算符的实现为什么会引起问题?

StringClass s;
s = s;

The textbook I'm reading (Absolute C++) says that after delete [] a; 我正在阅读的教科书(Absolute C ++)说, delete [] a;delete [] a; "The pointer sa is then undefined. The assignment operator has corrupted the object s and this run of the program is probably ruined." “然后指针sa是未定义的。赋值运算符破坏了对象s,并且该程序的运行可能已损坏。”

Why has the operator corrupted s? 为什么运算符损坏了s? If we're reinitalizing sa right after we delete it, why does this cause such a problem in the program that we have to redefine the function as: 如果删除后立即重新初始化sa,为什么会在程序中引起这样的问题,我们必须将函数重新定义为:

StringClass& StringClass::operator=(const StringClass& rtSide)
{
    if (this == &rtSide)
    //if the right side is the same as the left side
    {
        return *this;
    }
    else
    {
        capacity = rtSide.capacity;
        length = rtSide.length;
        delete [] a;
        a = new char[capacity];
        for (int i = 0; i < length; i++)
            a[i] = rtSide.a[i];

        return *this;
    }
}

If you are assigning an object to itself both a and rt.a point to the same string, so when you do delete [] a you are deleting both what a and rt.a point to; 如果您要为其本身分配一个对象,则art.a指向同一字符串,因此当您delete [] a您将同时删除art.a指向的内容; then you do reallocate it, but the data you were going to copy (on itself) in the loop has been lost in the delete . 那么您确实要重新分配它,但是要在循环中(自己复制)的数据已在delete丢失。

In the loop now you will just copy whatever junk happens to be in the memory returned by new on itself. 现在,在循环中,您将只复制new自身返回的内存中的所有垃圾。

By the way, even with the "safety net" of the self-assignment check that assignment operator isn't completely ok (for instance, it's not exception safe); 顺便说一句,即使使用了自赋值检查的“安全网”,赋值运算符也不是完全可以的(例如,它不是异常安全的); the "safe" way to define the "big three" (copy constructor, assignment operator, destructor) is using the " copy and swap idiom ". 定义“三巨头”(复制构造函数,赋值运算符,析构函数)的“安全”方法是使用“ 复制和交换习惯用法 ”。

If you self-assign, you free ( delete ) the string via the LHS argument before you copy it to the newly allocated space via the RHS argument. 如果您进行自我分配,请先通过LHS参数释放( delete )字符串,然后再通过RHS参数将其复制到新分配的空间。 This is not a recipe for happiness; 这不是幸福的秘诀。 it is undefined behaviour and anything may happen. 这是未定义的行为,可能会发生任何事情。 A crash is plausible; 崩溃是合理的; if you're really unlucky, it may appear to work. 如果您真的很不幸,它可能会起作用。

Consider what the value of rtSide.a is when you're inside the broken operator= . 考虑一下当您位于损坏的operator=内部时rtSide.a的值是什么。

It's the same as this->a , the values you just clobbered. this->a相同,您刚刚破坏的值。 Accessing non-owned memory is undefined behavior, thus accessing this->a is undefined behavior (since you just freed it). 访问非拥有的内存是未定义的行为,因此访问this-> a是未定义的行为(因为您刚刚释放了它)。

delete [] a;
a = new char[capacity];

for (int i = 0; i < length; i++)
   a[i] = rtSide.a[i]; //Invalid when this->a == rtSide.a 
   //because rtSide.a is no longer owned by your program.

If you did actually want to do this, you would have to make a copy of a before deleting it: 如果您确实要执行此操作,则必须先复制的副本,然后再删除它:

char* ca;
if (this == &rtSide) {
    ca = copy of rtSide.a or this->a;
} else {
    ca = rtSide.a;
}

//Do your assigning and whatnot

if (this == &rtSide) {
    delete[] ca;
}

Obviously it's much more efficient to just do nothing instead of making temporary copies of all of an instances own members. 显然,不做任何事而不是使所有实例的临时副本都拥有成员会更有效。 It's the same concept as doing int x = 5; int y = x; x = y; int x = 5; int y = x; x = y;概念相同int x = 5; int y = x; x = y; int x = 5; int y = x; x = y;

It is because you've first deleted the pointer delete [] a; 这是因为您首先删除了指针delete [] a;
and then later on trying to copy from the deleted location: 然后稍后尝试从已删除的位置进行复制:

for (int i = 0; i < length; i++)
       a[i] = rtSide.a[i]; //rtSide has already been deleted as 'this' and '&rtSide' are same.

Remember it is the same location you are trying to copy from, which you've already deleted. 请记住,它是您要从中复制的位置,您已经将其删除。 Hence, the error! 因此,错误!
The later code you posted fixes this problem by checking for self-assignment as a separate case. 您发布的以后的代码通过检查自赋值作为单独的案例来解决此问题。

delete [] a;
a = new char[capacity];

for (int i = 0; i < length; i++)
   a[i] = rtSide.a[i];

That's why. 这就是为什么。 Think of it like this: 这样想:

You delete whatever a points to, then allocate a new chunk of memory. 您删除任何指向的点,然后分配新的内存块。 The new chunk of memory contains garbage which becomes your new data. 新的内存块包含垃圾,这些垃圾将成为您的新数据。 Do not be confused by the loop that does a[i] = rtSide.a[i]; 不要被a[i] = rtSide.a[i];的循环所迷惑a[i] = rtSide.a[i]; that only copies the garbage onto itself. 只会将垃圾复制到自身上。

Remember, this and rtSide both lead you to the same object. 请记住, thisrtSide都将您引向同一个对象。 When you modify the object using this the object that rtSide refers to is modified. 使用this修改对象时, rtSide引用的对象将被修改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM