是否可以使用运算符重载按索引分配用户定义的数组?-C++

Is it possible to assign a user-defined Array by index with operator overloading? - C++

本文关键字:定义 用户 数组 -C++ 分配 索引 可以使 运算符 重载 是否      更新时间:2023-10-16


问题:当我尝试按索引分配 IntArray 对象时,出现以下错误:

"表达式不可分配。"

该错误由 iadrv.cpp 中的以下代码行生成:

IntArray a(10);
for(int i = a.low(); i <= a.high(); i++)
    a[i] = i * 10;

我可以像这样将整个 IntArray 对象分配给另一个对象,a = b; ,但是当特定索引被引用到"表达式不可分配"错误时,会发生"表达式不可分配"错误。

编辑:我从大多数函数中删除了const声明,并且不再收到"表达式不可分配"错误。但是,setName 现在给出错误:

"ISO C++ 11 不允许从字符串文字转换为'char *'"

此错误是由 iadrv.cpp 中的以下代码引起的:

a.setName("a");


程序说明:

我写了一个类 IntArray(C++),其中重载了以下运算符:

  • "[ ]" : 允许索引范围检查
  • "=" :允许数组分配
  • "+" :允许将两个数组的总和分配给第三个数组
  • "+=" :允许将两个数组的总和分配给第一个数组
  • "<<":允许输出数组的内容

该程序还包括以下功能:

  • setName :设置 IntArray 对象的名称
  • getName :返回 IntArray 对象的名称
  • 低:返回最小的合法索引
  • 高:返回最大的合法索引
  • 长度 : 返回元素数

驱动程序(iadrv.cpp,iadrv.h)将在IntArray类(IntArray.cpp,IntArray.h)上运行测试,以确定是否所有运算符都正确重载。

注意:对于每个数组测试数据,驱动程序将简单地将在每个数组初始化或修改并输出其内容后,数组索引立即为 10。当程序遇到运行时错误时,它应该使用适当的诊断"模拟"停止,而不是实际停止程序。


守则:

IntArray.h

//  IntArray.h
#ifndef __IntArray__IntArray__
#define __IntArray__IntArray__
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
class IntArray {
private:
    int a, b;
    int size;
    int * num;
    char * name;
public:
    IntArray(int start, int finish);
    IntArray(int finish = 10);
    IntArray(const IntArray &); //constructor copy
    ~IntArray();
    int low() const;
    int high() const;
    char * getName() const;
    //removed the const declaration from functions below
    int & operator [] (int);     //made to return int&
    friend ostream & operator << (ostream &, IntArray &);
    void setName(char *);
    int length() const;
    const IntArray & operator = (IntArray &);
    const IntArray & operator + (IntArray &);
    bool operator += (IntArray &);
};
#endif /* defined(__IntArray__IntArray__) */

IntArray.cpp

//  IntArray.cpp
#include "IntArray.h"
#include <iostream>
#include <fstream>
using namespace std;
extern ofstream csis;
IntArray::IntArray(int start, int finish) {
    if (start > finish) {
        cout << "Simulating a halt.";
        a = finish;
        b = start;
    }
    else {
        a = start;
        b = finish;
    }
    size = b-a;
    num = new int[size];
    name = new char[1];
    for (int i = 0; i < size; i++) {
        num[i] = 0;
    }
}
IntArray::IntArray(int finish) {
    size = finish;
    a = 0;
    b = finish - 1;
    num = new int[size];
    name = new char[1];
    for (int i = 0; i < size; i++) {
        num[i] = 0;
    }
}
IntArray::IntArray (const IntArray & right): size(right.size) {
    num = new int[size];
    name = new char[1];
    for (int i = 0; i < size; i++) {
        num[i] = right.num[i];
    }
}
IntArray::~IntArray() {
    delete[] num;
    delete [] name;
}
int IntArray::low() const{
    return a;
}
int IntArray::high() const{
    return b;
}
char * IntArray::getName() const{
    return name;
}
void IntArray::setName(char * n) {
    name = n;
}
//removed const declarations
//made to return int&
int & IntArray::operator [] (int subscript) const{
    if (subscript < a || subscript > b) {
        cout << "subscript: " << subscript << endl;
        cout << "Out of bound error. Simulating a halt." << endl;
        return num[a];
    }
    return num[subscript-a];
}
int IntArray::length() const{
    //b-a = size
    return (b-a);
}
//removed const declarations
ostream & operator << (ostream & output, IntArray & array) {
    for (int i = array.low(); i <= array.high(); i++) {
        output << array.name << "[" << i << "] = " << array[i] << endl;
    }
    return output;
}
//removed const declarations
IntArray & IntArray::operator = (IntArray & right) {
    if (length() == right.length()) {
        for (int i = 0; i <= length(); i++) {
            num[i] = right[right.low()+i];
        }
    return * this;
    }
    else {
        delete [] num;  //reclaim space
        delete [] name;
        size = right.length();
        num = new int [size]; //space created
        cout << "Different sized arrays. Simulating a hault" << endl;
    }
    return * this;
}
//removed const declarations
IntArray & IntArray::operator + (IntArray & right) {
    int * ptr;
    ptr = new int [right.length()];
    if (length() == right.length()) {
        for (int i = 0; i < length(); i++) {
            ptr[i] = num[i] + right[right.low()+i];
        }
    }
    return * this;
}
//removed const declarations
bool IntArray::operator += (IntArray & right) {
    if (length() == right.length()) {
        for (int i = 0; i <= right.length(); i++) {
            num[i] += right[right.low()+i];
        }
        return true;
    }
    cout << "Could not add the sum of the arrays into first array. Simulating a halt." << endl;
    return false;
}

IADRV.H

//  iadrv.h
#ifndef p6_iadrv_h
#define p6_iadrv_h
#include "intarray.h"
int main();
void test1();
void wait();
#endif

IADRV.cpp

//  iadrv.cpp
#include <iostream>
#include <iomanip>
#include <fstream>
#include <stdlib.h>
#include "iadrv.h"
using namespace std;
ofstream csis;
int main() {
    csis.open("csis.dat");
    test1();
    csis.close();
}
void test1() {
    system("clear");
    cout << "1. Array declared with single integer: IntArray a(10);" << endl << endl;
    csis << "1. Array declared with single integer: IntArray a(10);" << endl << endl;
    IntArray a(10);
    for(int i = a.low(); i <= a.high(); i++)
        a[i] = i * 10;
    a.setName("a");
    cout << a << endl;
    csis << a << endl;
    wait();
}

免责声明:该程序是作为学校作业编写的,并且已经上交进行评分。这是我的第一个 c++ 程序,所以我想了解我的错误。衷心感谢您的帮助。

你已经像这样定义了你的运算符[]:

const int operator [] (int) const;

第二个"const"意味着在该方法中你不能修改你的对象。

因此,它仅适用于获取值,而不适用于设置值。

尝试删除它,它应该可以工作。

编辑:正如Bryan Chen所指出的,您还需要返回引用和非常量,如下所示:

int& operator [] (int subscript)

现在,更深入地查看您的代码,这还不够,因为您有此方法:

ostream & operator << (ostream & output, const IntArray & array) {
    for (int i = array.low(); i <= array.high(); i++) {
        output << array.name << "[" << i << "] = " << array[i] << endl;
    }
    return output;
}

看起来你的 operator[] 需要在非常量 IntArray 上工作,但在该方法中,你的变量"array"是 const,所以你需要重写更多的代码。

另外,在其他运算符中寻找相同的问题,请记住:仅当您不打算从该方法内部修改对象时,您才创建方法"const",并且仅当您不打算修改该参数时才创建参数"const"。

现有运算符不允许更改值,因为它返回 int by 值,并且运算符声明为 const。 不能分配给值,只能分配给对象(包括引用,因为引用只是对象的另一个名称)。

要实现这一点,你需要用另一个非常量运算符来补充你现有的运算符,它返回对(非常量)int的引用:

int & operator[](int index);

由于此运算符将返回引用,因此可以使用要使用的熟悉的a[b] = c语法直接分配给返回值。

您不需要更改现有的运算符,但我强烈建议将返回类型从 const int 更改为仅 int - 无论如何您都是按值返回的,因此您要交回一份副本。 这是没有意义的,这可能会阻止编译器在数据类型比int更复杂的情况下省略副本。 (这里并没有太大区别,但我会避免养成按值常量返回的习惯,因为 - 假设存在复制构造函数 - 无论如何都可以通过简单地再次复制值来删除 const 限定符。返回 const 副本通常没有任何好处,但有几个缺点。

既然你也要求指出你的错误,我想评论一下你应该/可以做的两件事,以使代码更简单:

首先,赋值运算符可以这样写:

IntArray& operator=(IntArray rhs)
{
   std::swap(rhs.a, a);
   std::swap(rhs.b, b);
   std::swap(rhs.size, size);
   std::swap(rhs.num, num);
   std::swap(rhs.name, name);
   return *this;
}

这是有效的,因为您已经为IntArray定义了复制构造函数和析构函数,并且希望它们能够正常工作。 赋值运算符所做的只是创建一个临时对象,并将其内部与当前对象的内部交换。 然后临时数据与"旧数据"一起死亡,而新数据安全地在当前对象中。 这被称为copy/swap成语。

另请注意,返回的引用non-const

如果传递 const 引用而不是对象,则赋值运算符负责所做的初始复制。

IntArray& operator=(const IntArray& orig)
{
   IntArray rhs(orig);
   std::swap(rhs.a, a);
   std::swap(rhs.b, b);
   std::swap(rhs.size, size);
   std::swap(rhs.num, num);
   std::swap(rhs.name, name);
   return *this;
}

由于允许编译器优化传递值的副本,以前的版本可能会更快。 然而,传递 const 引用的第二种形式是通常执行的操作 - 请注意,在继续之前需要在函数内部创建临时对象。

其次,您的operator +可以使用operator +=

IntArray operator+(const IntArray& rhs)
{
   IntArray temp(*this);
   return temp += rhs;
}

我们所做的只是创建一个与当前对象相等的临时对象。 然后我们使用+=添加rhs并返回结果。 很好,很简单。 请注意,operator +返回一个新的 IntArray 对象,而不是 const IntArray。 此外,operator +=应返回对当前对象的引用,而不是bool

要利用这一点,应按以下方式重写您的operator +=

IntArray& operator+=(const IntArray& rhs)
{
  //..your current code goes here:
  //...
  return *this;
}

此外,您的operator +=不应该像那样"出错"。 您需要通过尝试添加两个大小可能不同的IntArray来使类更加健壮。 如果确实存在错误,则引发异常。 不要返回布尔值 - 从函数中删除return true;return false;。 始终返回*this