数组索引重载错误

Array Index Overload Bug

本文关键字:错误 重载 索引 数组      更新时间:2023-10-16

为什么此代码会导致数组的第二个元素打印为 0,而不考虑整数数组对象定义中指定的值? 下面代码的输出是 7 0 3 4 5 6 而不是 7 2 3 4 5 6,这种行为的原因是什么?

// Overloading operators for Array class
#include<iostream>
#include<cstdlib>
using namespace std;
// A class to represent an integer array
class Array
{
private:
int *ptr;
int size;
public:
Array(int *, int);
// Overloading [] operator to access elements in array style
int &operator[] (int);
// Utility function to print contents
void print() const;
};
// Implementation of [] operator.  This function must return a
// reference as array element can be put on left side
int &Array::operator[](int index)
{
if (index >= size)
{
cout << "Array index out of bound, exiting";
exit(0);
}
return ptr[index];
}
// constructor for array class
Array::Array(int *p = NULL, int s = 0)
{
size = s;
ptr = NULL;
if (s != 0)
{
ptr = new int[s];
for (int i = 0; i < s; i++)
ptr[i] = p[i];
delete ptr;
}
}
void Array::print() const
{
for(int i = 0; i < size; i++)
cout<<ptr[i]<<" ";
cout<<endl;
}
// Driver program to test above methods
int main()
{
int a[] = {1, 2, 3, 4, 5, 6};
Array arr1(a, 6);
arr1[0] = 7;
arr1.print();
arr1[8] = 6;
return 0;
}

Array构造函数中,在分配并填充动态分配的缓冲区后,立即在ptr处释放缓冲区

delete ptr;

在此点之后ptr对缓冲区的所有访问都会调用未定义的行为。旁注:应该delete[] ptr;此操作,以确保正确释放阵列。

解决方案:不要那样做!

添加析构函数以在Array超出范围并使用缓冲区完成时释放ptr

// destructor for array class
Array::~Array()
{
delete[] ptr;
}

编译器将自动生成析构函数,但该泛型析构函数无法知道delete[]指针成员中的内容是否安全。它可能不是一个数组,分配可能由另一个对象拥有(请参阅什么是资源或指针的所有权?(,或者可能没有使用new动态分配。

这带来了一个旁注:处理复制此对象的默认特殊成员函数将盲目地复制指针,而不是分配,并留下两个指向同一分配的对象。这迟早是致命的,因为一个副本将在另一个副本之前超出范围,如果没有其他副本尝试访问释放的分配并破坏程序,则第二个delete[]将破坏程序。这个问题及其解决方案在什么是三法则?

一般的经验法则是不要创建这样的类,而是使用std::vectorstd::vector做到了所有这些以及更多。

我修改了代码以包含显式默认构造函数和复制构造函数,还包括 std::out_of_range 异常,但不确定后者是否正确实现。 这是作为处理数组的练习而完成的,而不使用 STL 中的向量容器。 添加了一个交换成员函数和赋值运算符,但得到一些错误消息。

类"数组"没有成员"交换" 成员"数组::大小"(在第 12 行声明(不可访问 'operator=' 必须是成员函数 "this"只能在非静态成员函数中使用

// Overloading operators for Array class
#include<iostream>
#include<cstdlib>
//#include<vector>
using namespace std;
// A class to represent an integer array
class Array{
private:
int *ptr;
int size;
public:
Array(int *, int);
Array(const Array&);
~Array();
Array& operator= (Array);
// Overloading [] operator to access elements in array style
int &operator[] (int);
// Utility function to print contents
void print() const;
friend void swap(Array& first, Array& second);};

// Implementation of [] operator.  This function must return a
// reference as array element can be put on left side
int &Array::operator[](int index){
// try {
//     return ptr[index];}
// catch(const out_of_range& oor){
//         cerr << "Out of Range error: " << oor.what() << 'n';}    
if (index >= size || index < 0){
throw out_of_range("Index out of Range error");
}
return ptr[index];
}
// constructor for array class
Array::Array(int *p = NULL, int s = 0){
size = s;
ptr = NULL;
if (s != 0){
ptr = new int[s];
for (int i = 0; i < s; i++)
ptr[i] = p[i];}
}
// destructor for array class
Array::~Array(){
delete[] ptr;
ptr = NULL;}
// copy constructor for array class
Array::Array(const Array& A) { 
size = A.size;
ptr  = new int[size];
for (int i = 0; i < size; i++)
ptr[i] = A.ptr[i];}
void Array::swap(Array& first, Array& second){
using std::swap;
swap(first.size, second.size);
swap(first.ptr, second.ptr);}
//Assignment operator for array class
Array::Array& operator=(Array other){
swap(*this, other); 
return *this;}
//print function for array elements
void Array::print() const{
cout << "{";
for(int i = 0; i < size; i++)
cout<<ptr[i]<<" ";
cout<<"}"<<endl;}
// Driver program to test above methods
int main()
{
int a[] = {1, 2, 3, 4, 5, 6};
Array arr1(a, 6);
arr1[0] = 7;
arr1.print();
Array arr2 = arr1;
arr2.print();
arr1[-1] = 4;
return 0;
} 

修改了打印功能以排除最后一个数组元素之后的空格。 修改了构造函数方法声明以包含初始化的参数。 添加了 index[ ] 运算符重载的其他 const 版本,但不要认为它没有正确实现,或者它是否实际会被利用。

#include<iostream>
#include<cstdlib>

// A class to represent an integer array
class Array{
private:
int *ptr;
std::size_t size;
public:
Array(int *p = nullptr, std::size_t s = 0);
Array(const Array&);
~Array();
Array& operator= (Array);
// Overloading [] operator to access elements in array style
int &operator[] (std::size_t);
int const& operator[](std::size_t) const;
// Utility function to print contents
void print() const;
friend void swap(Array& first, Array& second);};

// Implementation of [] operator.  This function must return a
// reference as array element can be put on left side
int &Array::operator[](std::size_t index){
puts("overload");   
if (index >= size || index < 0){
throw std::out_of_range("Index out of Range error");
}
return ptr[index];
}
int const& Array::operator[](std::size_t index) const{
puts("const overload");
if (index >= size || index < 0){
throw std::out_of_range("Index out of Range error");
}
return ptr[index];
}

// constructor for array class
Array::Array(int *p, std::size_t s){
size = s;
ptr = nullptr;
if (s != 0){
ptr = new int[s];
for (int i = 0; i < s; i++){
ptr[i] = p[i];}
}
}
// destructor for array class
Array::~Array(){
delete[] ptr;
ptr = nullptr;}
// copy constructor for array class
Array::Array(const Array& A) { 
size = A.size;
ptr  = new int[size];
for (int i = 0; i < size; i++){
ptr[i] = A.ptr[i];}
}
//swap friend function of assignment operator
void swap(Array& first, Array& second){
using std::swap;
swap(first.size, second.size);
swap(first.ptr, second.ptr);}
//Assignment operator for array class
Array& Array::operator=(Array other){
swap(*this, other); 
return *this;}
//print function for array elements
void Array::print() const{
std::cout << "{";
for(int i = 0; i < size; i++){
std::cout << ptr[i];
if (i == size-1){
continue;}
std::cout<<" ";       
}
std::cout<<"}"<< std::endl;}
// Driver program to test above methods
int main()
{
int a[] = {1, 2, 3, 4, 5, 6};
Array arr1(a, 6);
std::cout << arr1[3] << 'n';
arr1[4] = 7;
arr1.print();
Array arr2 = arr1;
arr2.print();
arr1[-1] = 4;
return 0;
}