簡體   English   中英

分配超載:為什么使用同一對象會導致程序出現問題?

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

假設我們有以下內容:

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;
}

我的問題是:當我們嘗試向其自身分配對象時,這種重載賦值運算符的實現為什么會引起問題?

StringClass s;
s = s;

我正在閱讀的教科書(Absolute C ++)說, delete [] a;delete [] a; “然后指針sa是未定義的。賦值運算符破壞了對象s,並且該程序的運行可能已損壞。”

為什么運算符損壞了s? 如果刪除后立即重新初始化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;
    }
}

如果您要為其本身分配一個對象,則art.a指向同一字符串,因此當您delete [] a您將同時刪除art.a指向的內容; 那么您確實要重新分配它,但是要在循環中(自己復制)的數據已在delete丟失。

現在,在循環中,您將只復制new自身返回的內存中的所有垃圾。

順便說一句,即使使用了自賦值檢查的“安全網”,賦值運算符也不是完全可以的(例如,它不是異常安全的); 定義“三巨頭”(復制構造函數,賦值運算符,析構函數)的“安全”方法是使用“ 復制和交換習慣用法 ”。

如果您進行自我分配,請先通過LHS參數釋放( delete )字符串,然后再通過RHS參數將其復制到新分配的空間。 這不是幸福的秘訣。 這是未定義的行為,可能會發生任何事情。 崩潰是合理的; 如果您真的很不幸,它可能會起作用。

考慮一下當您位於損壞的operator=內部時rtSide.a的值是什么。

this->a相同,您剛剛破壞的值。 訪問非擁有的內存是未定義的行為,因此訪問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.

如果您確實要執行此操作,則必須先復制的副本,然后再刪除它:

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;
}

顯然,不做任何事而不是使所有實例的臨時副本都擁有成員會更有效。 int x = 5; int y = x; x = y;概念相同int x = 5; int y = x; x = y; int x = 5; int y = x; x = y;

這是因為您首先刪除了指針delete [] a;
然后稍后嘗試從已刪除的位置進行復制:

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

請記住,它是您要從中復制的位置,您已經將其刪除。 因此,錯誤!
您發布的以后的代碼通過檢查自賦值作為單獨的案例來解決此問題。

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

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

這就是為什么。 這樣想:

您刪除任何指向的點,然后分配新的內存塊。 新的內存塊包含垃圾,這些垃圾將成為您的新數據。 不要被a[i] = rtSide.a[i];的循環所迷惑a[i] = rtSide.a[i]; 只會將垃圾復制到自身上。

請記住, thisrtSide都將您引向同一個對象。 使用this修改對象時, rtSide引用的對象將被修改。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM