Понимание динамического выделения C ++ - PullRequest
3 голосов
/ 10 января 2011

Рассмотрим следующий код:

class CString
{
private:
    char* buff;
    size_t len;

public:
    CString(const char* p):len(0), buff(nullptr)
    {
        cout << "Constructor called!"<<endl;
        if (p!=nullptr)
        {
            len= strlen(p);
            if (len>0)
            {
                buff= new char[len+1];
                strcpy_s(buff, len+1, p);               
            }           
        }       
    }

    CString (const CString& s)
    {
        cout << "Copy constructor called!"<<endl;
        len= s.len;
        buff= new char[len+1];
        strcpy_s(buff, len+1, s.buff);      
    }

    CString& operator = (const CString& rhs)
    {
        cout << "Assignment operator called!"<<endl;
        if (this != &rhs)
        {
            len= rhs.len;
            delete[] buff;          
            buff= new char[len+1];
            strcpy_s(buff, len+1, rhs.buff);
        }

        return *this;
    }

    CString operator + (const CString& rhs) const
    {
        cout << "Addition operator called!"<<endl;

        size_t lenght= len+rhs.len+1;
        char* tmp = new char[lenght];
        strcpy_s(tmp, lenght, buff);
        strcat_s(tmp, lenght, rhs.buff);

        return CString(tmp);
    }

    ~CString()
    {
        cout << "Destructor called!"<<endl;
        delete[] buff;
    }     
};

int main()
{
CString s1("Hello");
CString s2("World");
CString s3 = s1+s2;     
}

Моя проблема в том, что я не знаю, как удалить память, выделенную в функции оператора сложения (char* tmp = new char[length]).Я не мог сделать это в конструкторе (я пробовал delete[] p), потому что он также вызывается из основной функции с массивами символов в качестве параметров, которые не выделяются в куче ... Как я могу обойти это?

Ответы [ 4 ]

4 голосов
/ 10 января 2011

Функция сложения должна возвращать CString, а не CString &.В функции сложения вы должны создать возвращаемое значение, а затем удалить [] temp, так как он больше не нужен, поскольку внутри класса CString вы делаете копию памяти.

CString operator + (const CString& rhs) const
{
    cout << "Addition operator called!"<<endl;

    size_t lenght= len+rhs.len+1;
    char* tmp = new char[lenght];
    strcpy_s(tmp, lenght, buff);
    strcat_s(tmp, lenght, rhs.buff);

    CString retval(tmp);
    delete[] tmp;
    return retval;
}
2 голосов
/ 10 января 2011

Проблемы:

В вашем операторе присваивания вы не предоставляете никаких гарантий исключений. Вы удаляете буфер, прежде чем гарантируете, что операция будет успешной. Если что-то пойдет не так, ваш объект останется в неопределенном состоянии.

CString& operator = (const CString& rhs)
{
    cout << "Assignment operator called!"<<endl;
    if (this != &rhs)
    {
        len= rhs.len;
        delete[] buff;          
        buff= new char[len+1];   /// BOOM 

        // If you throw here buff now points at undefined memory.
        // If this is an automatic variable the destructor is still going
        // to be called and you will get a double delete.

        // All operations that can fail should be done BEFORE the object is modified.

        strcpy_s(buff, len+1, rhs.buff);
    }

    return *this;
}

Мы можем исправить эти проблемы, перемещая вещи (и используя временную температуру).

CString& operator = (const CString& rhs)
{
    cout << "Assignment operator called!"<<endl;
    if (this != &rhs)
    {
        char* tmp = new char[len+1];
        strcpy_s(tmp, rhs.len+1, rhs.buff); // for char this will never fail
                                            // But if it was another type the copy
                                            // may potentially fail. So you must
                                            // do the copy before changing the curren
                                            // objects state.

        // Now we can change the state of the object safely.
        len= rhs.len;
        std::swap(tmp,buff);

        delete tmp;
    }

    return *this;
}

Еще лучшим решением является использование копии и замена idium:

CString& operator = (CString rhs) // Note pass by value to get auto copy.
{                                 // Most compilers will then do NRVO
    this->swap(rhs);
    // Simply swap the tmp rhs with this.
    // Note that tmp was created with copy constructor.
    // When rhs goes out of scope it will delete the object.
}

void swap(CString& rhs)
{
    std::swap(len,  rhs.len);
    std::swap(buff, rhs.buff);
}

Теперь давайте разберемся с вашим оператором +

CString operator + (const CString& rhs) const
{
    // You could optimize this by providing a private constructor
    // that takes two char pointers so that allocation is only done
    // once.
    CString result(*this);
    return result += rhs;
}

CString operator += (const CString& rhs)
{
    size_t lenght= len+rhs.len+1;

    // Char are easy. No chance of failure.
    // But if this was a type with a copy constructor or any other complex
    // processing involved in the copy then I would make tmp a smart pointer
    // to make sure that it's memory was not leaked if there was an exception.
    char* tmp = new char[lenght];

    strcpy_s(tmp, lenght, buff);
    strcat_s(tmp, lenght, rhs.buff);

    std::swap(len, length);
    std::swap(buff, tmp);

    delete tmp;
}
0 голосов
/ 10 января 2011

Прежде всего, operator+ должен возвращать новый объект, а не изменять один из операндов +, поэтому его лучше объявить как функцию, не являющуюся членом (возможно, другом).сначала внедрите operator+=, а затем используйте его - operator+, и у вас не возникнет этой проблемы.

CString operator+(CString const& lh, CString const& rh)
{
    CString res(lh);
    return res += rh;
}
0 голосов
/ 10 января 2011
    CString& operator + (const CString& rhs) const

{
    cout << "Addition operator called!"<<endl;

    size_t lenght= len+rhs.len+1;
    char* tmp = new char[lenght];
    strcpy_s(tmp, lenght, buff);
    strcat_s(tmp, lenght, rhs.buff);
    CString tempObj(tmp);
    delete [] tmp;
    return tempObj;
}

Например,

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...