Class - 用户定义的具有动态大小的智能阵列
Class - User Defined Smart Array with dynamic size
我正在编写以下数组 (class),当此数组的索引大于此数组的大小时,它会增加大小。 我知道向量,但它必须是数组。代码如下所示:
#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 个字节 free'd,
输出看起来像这样:0, 0, 3.14.
不幸的是,我必须编写此代码才能以第二种方式工作
( CustomArray[0] = CustomArray[1] = CustomArray[2] = 3.14;
)
你们能帮忙吗?提前致谢
您需要通过使用代理类型来解决这个问题,代理类型持有对 Array
对象的引用和传递给您的 operator[]
的索引。此代理类型将隐式转换为 float
并可从 float
分配,使访问(主要是 1)透明。
在这种情况下,我们也违反了三的规则,并实现了复制赋值运算符,将一个数组元素的值赋给另一个数组元素,以便 foo[0] = foo[1]
按预期工作。
我们需要进行以下更改:
- 重命名现有的
operator[]
并将其设为私有;它只会被代理类型使用。
- 创建一个新的
operator[]
returns 代理类型的值。
- 写代理类型。
更改 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
。)
1 这种方法的一个注意事项是在数组访问时使用类型推断(auto
in C++11):
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<bool>
专业化完全遵循我在此处给出的方法(返回代理对象),因此 it does have the same auto
caveat!您可以将其视为对这种方法的祝福。
直接或间接使用std::vector
。你的发言
I know about vectors but it has to be array.
没有意义。 std::vector
保证有连续存储,这可能就是你所说的"array"的意思。对于实例 v
,您始终可以使用表达式 &v[0]
来获取数组的基地址,并且从 C++11 开始,更易于阅读的 v.data()
也可以使用.这意味着您可以将 vector
用于任何需要数组 "C-style" 作为指针和大小的函数调用,例如qsort
.
如果 Array::operator []
中没有自动调整大小就不行,那么像你所做的那样制作一个 class 包装器,但在内部使用 std::vector
.这样更简单也更安全。特别是,您的代码具有二次最坏情况性能,例如以下将非常非常慢:
Array CustomArray;
for ( int i = 0; i < 1000000; ++i )
CustomArray[i] = i;
std::vector
是设计来不会有这个问题的
您提到的另一个问题,引用失效,可以通过使用 std::deque
轻松解决,但是 deque
没有连续存储。因此,对于 std::vector
,您仍然必须使用 cdhowie 所述的代理。但是,我必须承认我不太明白为什么语法必须是那样,或者手动调用 std::vector<float>::resize()
有什么问题。
我正在编写以下数组 (class),当此数组的索引大于此数组的大小时,它会增加大小。 我知道向量,但它必须是数组。代码如下所示:
#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 个字节 free'd,
输出看起来像这样:0, 0, 3.14.
不幸的是,我必须编写此代码才能以第二种方式工作
( CustomArray[0] = CustomArray[1] = CustomArray[2] = 3.14;
)
你们能帮忙吗?提前致谢
您需要通过使用代理类型来解决这个问题,代理类型持有对 Array
对象的引用和传递给您的 operator[]
的索引。此代理类型将隐式转换为 float
并可从 float
分配,使访问(主要是 1)透明。
在这种情况下,我们也违反了三的规则,并实现了复制赋值运算符,将一个数组元素的值赋给另一个数组元素,以便 foo[0] = foo[1]
按预期工作。
我们需要进行以下更改:
- 重命名现有的
operator[]
并将其设为私有;它只会被代理类型使用。 - 创建一个新的
operator[]
returns 代理类型的值。 - 写代理类型。
更改 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
。)
1 这种方法的一个注意事项是在数组访问时使用类型推断(auto
in C++11):
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<bool>
专业化完全遵循我在此处给出的方法(返回代理对象),因此 it does have the same auto
caveat!您可以将其视为对这种方法的祝福。
直接或间接使用std::vector
。你的发言
I know about vectors but it has to be array.
没有意义。 std::vector
保证有连续存储,这可能就是你所说的"array"的意思。对于实例 v
,您始终可以使用表达式 &v[0]
来获取数组的基地址,并且从 C++11 开始,更易于阅读的 v.data()
也可以使用.这意味着您可以将 vector
用于任何需要数组 "C-style" 作为指针和大小的函数调用,例如qsort
.
如果 Array::operator []
中没有自动调整大小就不行,那么像你所做的那样制作一个 class 包装器,但在内部使用 std::vector
.这样更简单也更安全。特别是,您的代码具有二次最坏情况性能,例如以下将非常非常慢:
Array CustomArray;
for ( int i = 0; i < 1000000; ++i )
CustomArray[i] = i;
std::vector
是设计来不会有这个问题的
您提到的另一个问题,引用失效,可以通过使用 std::deque
轻松解决,但是 deque
没有连续存储。因此,对于 std::vector
,您仍然必须使用 cdhowie 所述的代理。但是,我必须承认我不太明白为什么语法必须是那样,或者手动调用 std::vector<float>::resize()
有什么问题。