从重载运算符返回引用,并使用临时对象返回表达式

Returning reference from overloaded operatora and expression with temporary objects

本文关键字:返回 临时对象 表达式 重载 运算符 引用      更新时间:2023-10-16

我正在玩C++来提醒自己。所以我尝试了运算符 + 重载返回引用。这样做的动机是避免不必要的对象复制。看例子。我创建了类字符串和带有 + 的串联字符串。这只是实验性的,所以你会注意到一些丑陋的东西作为公共属性。

以下是代码的相关部分。

字符串.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 deletedn";
    for (int i = 0; i < 100; i++) {
        if (String::trash[i] != 0L) {
            wcout << String::trash[i]->getValue() << "n";
        }
    }
}

使用 VS2010 编译器和链接器在 CDT 中执行此操作,我得到了以下输出

创建(字符*):0:请

创建(字符*):1:我

已创建(字符*):2:删除

创建空:3

空载: 3:请删除

创建空:4

空加载:4:请删除我

请删除我

删除

4:请删除我

删除

2:删除

删除 1:我

删除 0:请

这些不会被删除

请删除

问题是为什么在表达式中创建的临时对象 请 + delStrRef + me; 不被删除。它不应该在表达式的末尾被删除,或者如果引用是临时对象而不是对象本身,它就会有所不同。

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