【问题标题】:Class - User Defined Smart Array with dynamic size类 - 具有动态大小的用户定义智能阵列
【发布时间】:2015-10-02 00:35:30
【问题描述】:

我正在编写以下数组(类),当这个数组的索引大于这个数组的大小时,它会增加大小。 我知道向量,但它必须是数组。代码如下所示:

#include <iostream>

using namespace std;

class Array {

public:
Array():_array(new float[0]), _size(0){};
~Array() {delete[] _array;}
friend ostream &operator<<(ostream&,const Array&);
float& operator[] (int index) 
{
  if(index>=_size)
  {
    float* NewArray=new float[index+1];
    for(int i=0;i<_size;++i) NewArray[i]=_array[i];
    for(int i=_size;i<index+1;++i) NewArray[i]=0; 
    delete[] _array;
    _array=NewArray;
    _size=index+1;
  }
  return _array[index];
}

private:
  float *_array; // pointer to array
  int _size; // current size of array
};

ostream &operator << ( ostream &out,  const Array& obj) // overloading operator<< to easily print array
{
  cout << "Array:\n\n";
  for (int i=0;i<obj._size;++i) 
  {
    cout << obj._array[i];
    if(i+1!=obj._size) cout << ", "; 
  }
  cout << ".\n";
  return out;
}

int main()
{
  Array CustomArray;
  CustomArray[2] = CustomArray[1] = CustomArray[0] = 3.14; // **here is the problem**
  cout << CustomArray << endl;
}

一切正常,0 个警告,0 个 valgrind 错误,输出:

3.14, 3.14, 3.14.

但是我必须以这种方式编写这段代码(在 main 中):

CustomArray[0] = CustomArray[1] = CustomArray[2] = 3.14;

现在是 3 个 valgrind 错误: 地址(some_address)是大小为 8 的块内的 4 个字节,

输出看起来像这样:0, 0, 3.14.

不幸的是,我必须编写此代码才能以第二种方式工作 (CustomArray[0] = CustomArray[1] = CustomArray[2] = 3.14;) 你们能帮忙吗?提前致谢

【问题讨论】:

  • 问题是CustomArray[1] 使所有先前的引用和指向数据的指针无效,因为它被重新分配了。你可以做的是返回一个ArrayAccess,它保存索引并且不保存引用但每次都访问原始数组。
  • 嗯,如果其中一个 operator[] 调用导致重新分配,我认为即使 std::vector 也无法应对......

标签: c++ arrays class memory-management operator-overloading


【解决方案1】:

您需要通过使用包含对Array 对象的引用和传递给operator[] 的索引的代理类型来解决此问题。此代理类型将隐式转换为 float 并可从 float 分配,从而使访问(主要是1)透明。

在这种情况下,我们也违反了三的规则,并实现了复制赋值运算符来将一个数组元素的值分配给另一个数组元素,以便foo[0] = foo[1] 按预期工作。

我们需要进行以下更改:

  1. 重命名现有的operator[] 并将其设为私有;它只会被代理类型使用。
  2. 创建一个新的operator[],它返回代理类型的值。
  3. 编写代理类型。

更改 1,在 Array 的定义内:

friend class ArrayElement; // So that ArrayElement can use access()
private:
float& access(int index) 
{
  if(index>=_size)
  {
    float* NewArray=new float[index+1];
    for(int i=0;i<_size;++i) NewArray[i]=_array[i];
    for(int i=_size;i<index+1;++i) NewArray[i]=0; 
    delete[] _array;
    _array=NewArray;
    _size=index+1;
  }
  return _array[index];
}

变化2:

// Inside of Array
public:
    ArrayElement operator[](int index);

// Implementation outside of Array
ArrayElement Array::operator[](int index) {
    return ArrayElement(*this, index);
}

变化3:

class ArrayElement
{
    friend class Array; // So that Array can use our private constructor

private:
    ArrayElement(Array & array, int index) : array(array), index(index) { }

public:
    // Allows "foo[1] = 2"
    ArrayElement const & operator=(float v) const {
        array.access(index) = v;
        return *this;
    }

    // Violation of the rule of three, but it makes sense in this case.
    // Allows "foo[1] = foo[2]"
    ArrayElement const & operator=(ArrayElement const & other) const {
        array.access(index) = other;
        return *this;
    }

    // Allows "float x = foo[1]"
    operator float() const {
        return array.access(index);
    }

private:
    Array & array;
    int index;
};

(最后的小改动,需要在Array的定义前转发声明ArrayElement。)

See this working example.


1 这种方法的一个注意事项是对数组访问使用类型推断(C++11 中的auto):

auto x = an_array[1];

现在x 是ArrayElement 而不是float,当an_array[1] 发生变化时,它的值会发生变化。尝试为x 分配不同的浮点值也会改变an_array[1] 中的值,因为x 只是该值的代理。

将此与std::vector 的一般行为进行对比,其中auto x = a_vector[0] 将导致x 成为向量的元素类型,因此将保存存储在向量中的值的独立副本。

但是请注意,std::vector&lt;bool&gt; 特化完全遵循我在此处给出的方法(返回代理对象),因此it does have the same auto caveat!您可以将此视为对这种方法的一种祝福。

【讨论】:

    【解决方案2】:

    直接或间接使用std::vector。你的陈述

    我知道向量,但它必须是数组。

    没有意义。 std::vector 保证有连续存储,这可能就是你所说的“数组”。对于实例v,您始终可以使用表达式&amp;v[0] 来获取数组的基地址,并且从C++11 开始,更易于阅读的v.data() 也可以使用。这意味着您可以将vector 用于任何需要数组“C 样式”作为指针和大小的函数调用,例如qsort.

    如果你不能在Array::operator [] 中自动调整大小,那么就像你所做的那样制作一个类包装器,但是在内部使用std::vector 。这更加简单和安全。特别是,您的代码具有二次最坏情况性能,例如以下会非常非常慢:

    Array CustomArray;
    for ( int i = 0; i < 1000000; ++i )
         CustomArray[i] = i;
    

    std::vector 旨在没有有这个问题。

    您提到的另一个问题是引用失效,可以通过使用std::deque 轻松解决,但是deque 没有连续存储。因此,对于std::vector,您仍然必须使用 cdhowie 所描述的代理。但是,我必须承认我不太明白为什么语法必须这样,或者手动调用 std::vector&lt;float&gt;::resize() 有什么问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-06-10
      • 2015-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多