C++具有自定义类的动态 2D 数组

C++ Dynamic 2D Array with Custom Classes

本文关键字:动态 2D 数组 自定义 C++      更新时间:2023-10-16

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

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

#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;
}

下面是 2D 数组类实现的代码:

#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
}

如果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 数组

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

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);
    }
}

您的代码仍然存在一些问题。

Issue 1:

如果 TwoDArray 是一个 const 对象,你的运算符 [] 将不会编译。 这是一个例子

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

在这里有一个 const TwoDArray 是完全合理的,因为你没有改变数组的内部结构。 因此,您需要重载运算符 [] 两次,一次用于常量对象,另一次用于非常量对象。 所以第二个重载应该看起来像这样:

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: 你想有一个使用运算符 [] 的 2D 数组吗?
有人反对使用 [][] 来表示二维矩阵,而是让operator()表示索引。

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

你的 TwoDArray 构造函数在这里是有缺陷的: for (int i = 0; i <10; i++( { p[i] = new MyArray[10]; }

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

然后从那里继续!

如果我理解正确的代码,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];

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

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