【问题标题】:C++ Dynamic 2D Array with Custom Classes具有自定义类的 C++ 动态二维数组
【发布时间】:2014-04-29 20:27:35
【问题描述】:

该程序的目标是从一维数组类创建一个复合二维数组类,利用指针和operator[] 在主程序中使用。我们被告知让[][] 像标准二维数组一样工作。我让它编译但是当我使用2DArray 类时它崩溃了。我知道目标是理解指针,但我认为 operator[] 给我带来了最大的麻烦。任何见解将不胜感激。

编辑:适用于标准数组。但是,如果我能以简单的方式使用该类,我会很高兴,例如:

#include <iostream>
#include "MyArray.h"
#include "TwoDArray.h"

using namespace std;

int main()
{
TwoDArray test(4,5);
cout << "Test output:  "<< test[3][2] << endl;
return 0;
}

这里是二维数组类实现的代码:

#include <iostream>
#include <cstdlib>
#include "TwoDArray.h"
#include "MyArray.h"


TwoDArray::TwoDArray()
{
    //Default Constructor
    row = 10;
    col = 10;
    MyArray** p = new MyArray* [10];
    for (int i = 0; i < 10; i++)
    {
        p[i] = new MyArray[10];
    }
}

TwoDArray::TwoDArray (int r, int c)
{
    row = r;
    col = c;
    MyArray** p = new MyArray* [col];
    for (int i = 0; i < col; i++)
    {
        p[i] = new MyArray[row];
    }
}

TwoDArray::~TwoDArray() //Destructor 
{
    for (int i = 0; i < row; i++)
    {
        delete [] &p[i];
    }
    delete [] p;
}

MyArray & TwoDArray::operator[] (int pos)
{
    if( pos < 0 || pos >= col )
    {
        cout << "Illegal index, pos = " << pos << endl;
    }
    return *p[pos];
}

以及导师给出的1DArray(MyArray)类实现:

#include <iostream>
#include <cstdlib>
#include "MyArray.h"    // "" around header means "look in current directory first"

// default to an array of 10 integers, fill with 0
MyArray::MyArray()
{
    int i;
    _a = new int[10];    // new allocates RAM from system heap, [] says allocate an array
    _n = 10;
    for( i = 0; i < 10; i++ )
    {
        _a[i] = 0;       // initialize array to all 0
    }
}

// allocate array of a size requested by the client if legal, fill with 0
MyArray::MyArray( int num )
{
    int i;
    if( num <= 0 ) // if illegal, set to default
    {
        num = 10;
    }
    _a = new int[num];
    _n = num;
    for( i = 0; i < num; i++ )
    {
        _a[i] = 0;       // initialize array to all 0
    }
}

// copy constructor - invoke deep copy asignment
MyArray::MyArray( const MyArray &m )
{
   *this = m;
}

// destructor - needed to deallocate RAM allocated in constructors
MyArray::~MyArray()
{
    delete[] _a;
}

// get value at position pos
int &MyArray::At( int pos )
{
    if( pos < 0 || pos >= _n )
    {
        cout << "Illegal index, pos = " << pos << endl;
        exit( -1 );
    }
    return _a[pos];
}

// get value at position pos using [] indexing operator
int & MyArray::operator []( int pos )
{
    cout << "1D [] pos = " << pos << "_n is " << _n << endl;
    if( pos < 0 || pos >= _n )
    {
        cout << "Illegal index, pos = " << pos << endl;
        exit( -1 );
    }
    return _a[pos];
}

// return size, const here means it cannot change self
int MyArray::size( void ) const
{
    return _n;
}

// deep copy - REQUIRED if allocated RAM is used by object!
MyArray &MyArray::operator =( const MyArray &rhs )
{
    int i;
    if( &rhs == this ) // assignment to self?
    {
        return *this;  // if so, don't assign, just return self
    }
    if( rhs._n != _n )   // rhs not the same size as myself?
    {
        delete[] _a;    // yes, clear out my data and reallocate to match
        _a = new int[rhs._n];
        _n = rhs._n;
    }
    for( i = 0; i < rhs._n; i++ )  // copy all elements
    {
        _a[i] = rhs._a[i];
    }
    return *this;      // allow a = b = c; assignment
}

【问题讨论】:

  • 你能告诉我们你调用的崩溃代码吗?
  • 除了提到的析构函数问题 cppguy 之外,您的二维数组似乎也缺少复制构造函数和复制赋值运算符。

标签: c++ arrays pointers operator-overloading composition


【解决方案1】:

如果MyArray 本身是一维数组,那么我用MyArray 初始化二维数组时你做错了。我没有描述细节,但我认为你需要做这样的事情 -

TwoDArray::TwoDArray()
{
//Default Constructor
row = 10;
col = 10;
MyArray* p = new MyArray [10];
for (int i = 0; i < 10; i++)
{
    p[i] = new MyArray(10);
} }

TwoDArray::TwoDArray (int r, int c)
{
row = r;
col = c;
MyArray* p = new MyArray [col];
for (int i = 0; i < col; i++)
{
    p[i] = new MyArray(row);
}
}

请注意,我使用 1D 数组更改了 2D 初始化,每个数组在内部都是 MyArray 对象,它本身就是一个数组 - 因此是 2D 数组

【讨论】:

  • 不确定为什么要传递参数rc,然后在内部分配给rowcol。为什么不只有参数rowcol?有什么我想念的吗?也许这个问题对 OP 来说比对你更重要……但你保留了它。
  • @Floris 好点子,实际上我并没有刻意优化它,因为它是一个大学学习项目,所以没有为 OP 完成它。我只是想向他指出分配问题。 :) 但感谢您指出。
【解决方案2】:

你的析构函数有点想多了。试试这个:

TwoDArray::~TwoDArray() //Destructor 
{
    delete [] p;
}

您也没有将分配的指针存储在构造函数中。您正在将分配的内存分配给您在堆栈上声明的指针。不是你的会员p

您还分配了一个 2D 数组数组,这将构成一个 3D 数组

在您的构造函数中,为您的成员 p 分配一个 MyArray 数组,如下所示:

TwoDArray::TwoDArray()
{
    //Default Constructor
    row = 10;
    col = 10;
    p = new MyArray[row]; // where p is a MyArray* member of TwoDArray
    for (int i = 0; i < row; i++)
    {
        p[i] = MyArray(col);
    }
}

TwoDArray::TwoDArray (int r, int c)
{
    row = r;
    col = c;
    p = new MyArray[row]; // where p is a MyArray* member of TwoDArray
    for (int i = 0; i < row; i++)
    {
        p[i] = MyArray(col);
    }
}

【讨论】:

  • 感谢关于析构函数的提示。这里有很多好的建议。
  • 您是否尝试过运行valgrind 或其他实用程序来确认您的“简单析构函数”不会留下一些无法访问的内存?我原以为您需要在销毁指向它们的 2D 数组之前删除单个 1D 数组。但我来自 C 背景——也许 C++ 比这更聪明……
  • 如果 MyArray 的析构函数没有清理它自己的内部,这是一个可以轻松修复的错误。但是 delete[] 保证被删除数组中每个项目(在本例中为 ~MyArray)的所有析构函数都会被调用
【解决方案3】:

您的代码仍有一些问题。

Issue 1:

如果 TwoDArray 是 const 对象,您的 operator[] 将不会编译。这是一个例子

void foo(const TwoDArray& arr)
{
   int x = arr[0][0];  // error.  operator [] must be const
}

这里有一个 const TwoDArray 是完全合理的,因为您不会更改数组的内部结构。因此,您需要重载运算符 [] 两次,一次用于 const 对象,另一次用于非 const。所以第二个重载应该是这样的:

const MyArray & TwoDArray::operator[] (int pos) const
{
    if( pos < 0 || pos >= col )
    {
        cout << "Illegal index, pos = " << pos << endl;
    }
    return *p[pos];
}

但是,这里还有一个问题,我会在下面的Issue 3 中显示。

Issue 2: 如果new[] 失败,您的赋值运算符将损坏this

在您的赋值运算符中,您调用delete[] _a。如果随后对new [] 的调用引发异常会怎样?您现在已经损坏了对象,因为您删除了内存,因此您无法恢复已删除的数据。

至少在你的实现中应该做的是

  1. 先分配新内存,再分配一个临时指针,
  2. 将数据从传入的对象移动到新内存中,
  3. 删除旧内存(在这种情况下,这是你最终调用delete [] _a;的地方)
  4. 将步骤 1 中的临时指针分配给 _a

这样,如果第 1 步失败,则会引发异常,并且您的对象不会损坏。

Issue 3:在你的operator []中,如果做了越界访问,你还是继续执行非法访问。

如果您想让该课程的用户在他们提供越界访问权限的情况下自取其辱,那么我认为您应该删除cout 消息并继续允许非法访问。如果您确实想要进行边界检查访问,请使用可以输出的 at() 函数,并在越界访问时使用 throw an exception

请注意,std::vector 是这样做的——operator [] 未选中,而 std::vector::at() 已选中,会引发错误。

Issue 4:。风格——当心变量名以下划线开头。

带下划线的名称是为编译器的实现保留的。我知道有些情况下下划线名称是安全的,但我总是谨慎行事,从不引入以下划线开头的名称。

Issue 5:你想要一个使用 operator[] 的二维数组吗?
有人反对使用 [][] 表示二维矩阵,而是使用 operator() 表示索引。

请看这里:http://www.parashift.com/c++-faq/matrix-subscript-op.html

【讨论】:

    【解决方案4】:

    您的 TwoDArray 构造函数在这里存在缺陷: for (int i = 0; i

    您实际上是在创建一个 3D 数组!! 将其更改为 p[i] = new MyArray(10);

    然后从那里继续!

    【讨论】:

      【解决方案5】:

      如果我正确理解您的代码,MyArray 代表TwoDArray 的一行。在这种情况下,TwoDArray 的构造函数不会使用正确的参数创建 MyArray 对象。

      你有:

      TwoDArray::TwoDArray()
      {
          //Default Constructor
          row = 10;
          col = 10;
          MyArray** p = new MyArray* [10];
          for (int i = 0; i < 10; i++)
          {
              p[i] = new MyArray[10];
          }
      }
      

      首先,我会将10 的用法改为适当地使用rowcol

      TwoDArray::TwoDArray()
      {
          //Default Constructor
          row = 10;
          col = 10;
          MyArray** p = new MyArray* [row];
          for (int i = 0; i < 10; i++)
          {
              p[i] = new MyArray[col]; // This is a problem line. You are creating col
                                       // MyArray objects. I think you should create one
                                       // MyArray object with col items in it.
      
              p[i] = new MyArray(col); // This is what you want.
          }
      }
      

      但真正的问题是你有:

          MyArray** p = new MyArray* [row];
      

      这是一个局部变量。当您从函数返回时,它会消失。成员变量p 保持未初始化状态。你的意思可能是:

          p = new MyArray* [row];
      

      您可以类似地更改其他构造函数。

      cppguy 已经指出了析构函数的问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-06
        • 1970-01-01
        • 2011-01-11
        • 2016-03-18
        • 1970-01-01
        相关资源
        最近更新 更多