在 C++ 中重载运算符 + 以添加两个数组

Overloading operator + in c++ to add two arrays

任务是重载运算符 + 以便它添加两个数组。 Array 是一个 class 有两个私有成员 int* data 和 int m(capacity of array).

void Array::setM(int m){
this->m = m;
this->data = new int[this->m];
}

int& Array::operator[](int i){
return this->data[i];
}


Array& Array::operator+(Array& a){
Array res;
if (this->m >= a.m) {
    res.setM(this->m);
    for (int i=0; i<this->m; i++){
    res.data[i] = this->data[i] + a.data[i];
    }
}
else if (this->m < a.m) {
    res.setM(a.m);
    for (int i=0; i<a.m; i++){
    res.data[i] = this->data[i] + a.data[i];
    }
}
Array& rez = res;
return rez;
}

这是主要的:

int main()
{
Array a(3);
Array& a1 = a;
a1[0] = 1;
a1[1] = 2;
a1[2] = 4;

Array b(3);
Array& b1 = b;
b1[0] = 1;
b1[1] = 2;
b1[2] = 4;

Array& c = a1.operator+(b1);
for (int i=0; i<3; i++){
    cout<<c[i]<<" ";
}
return 0;
}

函数在 return 类型为 Array 时工作正常,但是当 return 类型为 Array& 它 returns 173104 4200736 4200896。我从 C++ 开始,所以引用混淆我有点,没看到点函数returning引用类型?

你在这里违反了规则,你得到了所谓的未定义行为。您的程序可能会因分段错误而崩溃,或者它会输出垃圾,就像您观察到的那样。

你看,你正在 return引用局部变量。这个不好。让我解释一下。

你在函数内部声明的变量通常有自动存储。这意味着它们有一个明确定义的存在开始(声明)和存在结束,即其范围的结束。在你的例子中,这是函数的结尾:

Array& Array::operator+(Array& a) {
    Array res;
    if (this->m >= a.m) {
        res.setM(this->m);

        for (int i=0; i<this->m; i++){
            res.data[i] = this->data[i] + a.data[i];
        }
    } else if (this->m < a.m) {
        res.setM(a.m);

        for (int i = 0; i < a.m; i++) {
            res.data[i] = this->data[i] + a.data[i];
        } // <-- here, `i` dies.
    }

    Array& rez = res;
    return rez;
} // <-- here, `res` dies.

所以引用rez指向一个死变量。阅读它被标准归类为未定义行为。

显而易见的解决方案是 return 按值,您还应该对参数进行 const 引用:

 Array Array::operator+(Array const& a) {
    Array res;
    
    // [...]

    return res;
}

在这种情况下,编译器将复制(或移动)res 对象。请记住,编译器非常擅长他们的工作,并且很可能会在这种特殊情况下应用复制省略。也就是说不会有copy,直接在return通道中构造变量res

让我们逐步考虑您的运算符实施。

对于初学者来说,右手操作数在运算符中没有改变。所以该函数至少应该声明为

Array& operator+( const Array& a );

该函数在其函数体内创建了一个 Array 类型的新对象。

Array res;

因此,由于该对象是函数的局部变量,您不能 return 引用它。这意味着运算符应该声明为

Array operator+( const Array& a );

const Array operator+( const Array& a );

这段代码

if (this->m >= a.m) {
    res.setM(this->m);
    for (int i=0; i<this->m; i++){
    res.data[i] = this->data[i] + a.data[i];
    }
}

a.m 小于 this->m 时可以调用未定义的行为。

由于成员函数不改变左操作数,因此该函数应该是常量成员函数。

const Array operator +( const Array& a ) const;

通常这样的运算符被声明为 class 的独立友元函数。

friend const Array operator+( const Array &a, const Array &b );

考虑到您在运算符中使用的算法,可以定义它(在 class 定义之外。或者如果您想在 class 定义中定义它,那么您需要使用说明符 friend) 以下方式

const Array operator+( const Array &a, const Array &b )
{
    int m = std::min( a.m, b.m );
    Array res( m );

    for ( int i = 0; i < m; i++ )
    {
        res.data[i] = a.data[i] + b.data[i];
    }

    return res;
}

注意函数 setM 会产生内存泄漏,因为它不会删除为 Array

类型的对象提前分配的内存
void Array::setM(int m){
this->m = m;
this->data = new int[this->m];
}

那是函数不安全。

还请记住,带参数的构造函数应使用函数说明符 explicit 进行声明。否则,您可能会遇到整数到 Array.

类型的意外转换

当您动态分配数组时,您至少需要显式编写析构函数、复制构造函数和复制赋值运算符。