【问题标题】:Is it possible to assign a user-defined Array by index with operator overloading? - C++是否可以使用运算符重载按索引分配用户定义的数组? - C++
【发布时间】:2014-11-21 04:02:44
【问题描述】:


问题: 当我尝试按索引分配 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 对象的名称
  • low : 返回最小的合法索引
  • high : 返回最大的合法索引
  • length : 返回元素个数

驱动程序(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++ 程序,所以我想了解我的错误。衷心感谢您的帮助。

【问题讨论】:

  • 您的operator= 太复杂了。它可以写得比你尝试的要简单得多。此外,您的operator+ 可以写成只调用operator +=

标签: c++ arrays operator-overloading


【解决方案1】:

你已经像这样定义了你的 operator[]:

const int operator [] (int) const;

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

所以它只能用于获取值,而不能用于设置值。

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

编辑:AS 指向 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[] 需要在一个非 const IntArray 上工作,但在那个方法中你的变量“array”是 const,所以你需要重写一些代码。

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

【讨论】:

  • 这还不够。您应该提供返回int&amp; 的重载。第一个 const 没有任何意义
  • @DWilches 我从 operator[] 中删除了第二个“const”。但是,我的 ostream 和 operator= 函数现在抱怨:“对于类型 'const IntArray',没有可行的重载 operator[]。我需要从所有这些函数中删除 const 声明吗?
  • @BryanChen 你能举一个返回 int& 的例子吗?我以为我是通过将变量 name 声明为指针并返回它的值来做到这一点的。
  • 通常会有两个operator[] 重载,一个是 const,一个不是:int operator[](int index) const; int &amp; operator[](int index);。这提供了一个读写的非常量实现和一个只读的常量实现。
  • @DWilches 好的,我会尝试这些更新的。我最初包含“const”是因为实验室要求“一旦创建了 IntArray 对象,就不能更改其大小”。从 operator[] 和 ostream 中删除“const”是否仍然允许这样做?
【解决方案2】:

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

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

int & operator[](int index);

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

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

【讨论】:

    【解决方案3】:

    由于您还要求指出您的错误,我想评论一下您应该/可以做的两件事以使代码更简单:

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

    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 += 不应该像那样“出错”。您需要通过尝试添加两个大小可能不同的IntArrays 来使该类更加健壮。如果确实有错误抛出异常。不要返回布尔值——从函数中完全删除 return true;return false;。总是返回*this

    【讨论】:

    • IntArray&amp; operator=(IntArray rhs) 模式通常被认为是糟糕的设计,我相信。例如,它可以防止您尝试为自身分配值的快速无操作,并且它会创建一个额外的中间对象,其中一个不是绝对必要的。它表现正确,但表现不佳。它还需要存在复制构造函数,这并不总是正确的假设。
    • @cdhowie - 有人声称对于赋值运算符和复制/交换,传递对象将允许编译器进行复制,从而利用可能存在的任何优化。另外,我假设 IntArray 类具有正确的复制构造函数,这就是我在答案中提到的(如果没有有效的复制构造函数,复制/交换将无法工作)。
    • 从这里进行研究:stackoverflow.com/questions/3279543/…
    • @cdhowie 我确实有一个复制构造函数。鉴于此,您还会建议不要使用 IntArray&amp; operator=(IntArray rhs) 模式吗?
    • @cjan92127 - 您可以通过传递 const 引用来使用 cdhowie 建议的内容。但是,代码需要稍作改动,但幅度不大。底线是你有一个有效的复制构造函数和析构函数,所以无论你选择什么,复制/交换都可以工作。
    猜你喜欢
    • 2013-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-01
    相关资源
    最近更新 更多