Возврат ссылки из перегруженного оператора и выражения с временными объектами

Я играю с С++, чтобы напомнить себе об этом. Поэтому я экспериментировал с оператором +, перегружая возвращаемую ссылку. Мотивом этого является избежание ненужного копирования объектов. Посмотрите на пример. Я создал класс String и объединил строки с +. Это был просто эксперимент, поэтому вы заметите некоторые уродливые вещи в качестве общедоступных атрибутов.

Вот соответствующие части кода.

Строка.hpp

#ifndef STRING_HPP_
#define STRING_HPP_

#include <iostream>

using namespace std;

#ifndef CPP11
    typedef wchar_t unicode16;
#else
    typedef char16_t unicode16;
#endif //CPP11

class String {

    unicode16 * value;
    unsigned strLength;

    void initEmpty() {
        value = 0L;
        strLength = 0;
    }

    static unsigned counter;
public:

    static String ** trash;

    unsigned id;

    String::String() : value(0L), strLength(0){
        id=counter++;
        trash[id]=this;
        cout << "Creating empty: " << id << "\n";
    }

    String::String(const String &str);

    String(const char *);

    String(const unicode16 *);

    unsigned length() const {
        return strLength;
    }

    ~String() {

        wcout << L"Deleting " << id << ": " << value << L"\n";
        trash[id]=0L;
        delete value;
    }

    String & operator +(String &);

    unicode16 * getValue() {
        return value;
    }
};

#endif /* STRING_HPP_ */

Строка.cpp

#include "String.hpp"
#include "../exception/IllegalArgumentException.h"
#include <string.h>


unsigned String::counter = 0;

String ** String::trash = new String *[100]();

String::String(const String & str) {
    value = new unicode16[str.strLength + 1];
    strLength = str.strLength;
    for(int i = 0; i < strLength ; i++) {
        value[i] = str.value[i];
    }
    value[strLength] = 0;
    id = counter++;trash[id]=this;
    wcout << L"Created (copy): " << id << ": " << value << L"\n";
}

String::String(const char *charArray) {
    if (charArray == 0L) {
        throw IllegalArgumentException("Char array pointer is null");
    }
    strLength = strlen(charArray);
    value = new unicode16[strLength + 1];

    for (int i = 0; i < strLength; i++) {
        value[i] = (unicode16)charArray[i];
    }
    value[strLength] = 0;
    id = counter++;trash[id]=this;
    wcout << L"Created (char *): " << id << ": " << value << L"\n";
}

String::String(const unicode16 *utfArray) {
    if (utfArray == 0L) {
        throw IllegalArgumentException("Unicode array pointer is null");
    }
    strLength = wcslen(utfArray);
    value = new unicode16[strLength + 1];

    for (int i = 0; i < strLength; i++) {
        value[i] = utfArray[i];
    }
    value[strLength] = 0;
    id = counter++;
    trash[id]=this;
    wcout << L"Created (unicode): " << id << ": " << value << L"\n";
}

String & String::operator +(String &str) {
    unsigned newLength = length() + str.length();
    unicode16 * newArray = new unicode16[newLength + 1];
    wcscpy(newArray, value);
    wcscpy(newArray + strLength, str.value);

    String * strPointer = new String();
    strPointer->value = newArray;
    strPointer->strLength = newLength;

    String &result = *strPointer;
    wcout << L"Empty loaded: " << result.id << ": " << result.value << L"\n";
    return result;
}

И основной метод

#include "../string/string.hpp"
#include <iostream>

using namespace std;



int metodica(void) {
    String & please = String("Please");
    String meString = "me";
    String & me = meString;
    String & delStrRef = String(" delete ");

    String & result1 = please + delStrRef + me;

    wcout << result1.getValue() << L"\n";
    delete &result1;
    return 0;
}


int main(void) {
    metodica();
    cout << "These are not deleted\n";
    for (int i = 0; i < 100; i++) {
        if (String::trash[i] != 0L) {
            wcout << String::trash[i]->getValue() << "\n";
        }
    }
}

Выполняя это в CDT с использованием компилятора и компоновщика VS2010, я получил следующий результат

Создано (знак *): 0: Пожалуйста

Создано (знак *): 1: я

Создано (знак *): 2: удалить

Создание пустых: 3

Пустой загружен: 3: Пожалуйста, удалите

Создание пустых: 4

Пусто загружено: 4: Пожалуйста, удалите меня

Пожалуйста, удалите меня

Удаление 4: Пожалуйста, удалите меня

Удаление 2: удалить

Удаление 1: я

Удаление 0: Пожалуйста

Эти не удаляются

Пожалуйста, удалите

Вопрос в том, почему временный объект создается в выражении Please + delStrRef + me; не удаляется. Разве он не должен быть удален в конце выражения или он идет по-другому, если ссылка является временным объектом, а не самим объектом.


person ognjenmi    schedule 19.10.2013    source источник
comment
Прочтите это и this, а затем прекратите писать уродливый код. Вы ничего себе не экономите.   -  person Benjamin Lindley    schedule 20.10.2013
comment
Это было намеренно некрасиво, потому что я хотел быстро увидеть, что на самом деле делается с временными объектами. Это, конечно, не для производства или что-то серьезное.   -  person ognjenmi    schedule 20.10.2013
comment
Но все равно спасибо за статьи.   -  person ognjenmi    schedule 20.10.2013


Ответы (1)


String & String::operator +(String &str) {
    ...
    String * strPointer = new String();
    ...
    String &result = *strPointer;
    ...
    return result;
}

Вы вручную создали объект, а затем вернули ссылку на него. Следовательно, компилятор не вставил оператор delete. Таким образом, вы не можете вернуть ссылку без утечки памяти и ручного уничтожения объекта.

Например в Qt библиотека operator+ реализована как

Q_EXPORT inline const QString operator+( const QString &s1, const QString &s2 ) {
    QString tmp( s1 );
    tmp += s2;
    return tmp; 
}

Где operator+= объявлено как QString &operator+=( const QString &str );

person Ilmirus    schedule 19.10.2013