在 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
.
类型的意外转换
当您动态分配数组时,您至少需要显式编写析构函数、复制构造函数和复制赋值运算符。
任务是重载运算符 + 以便它添加两个数组。 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
.
当您动态分配数组时,您至少需要显式编写析构函数、复制构造函数和复制赋值运算符。