数组索引重载错误
Posted
技术标签:
【中文标题】数组索引重载错误【英文标题】:Array Index Overload Bug 【发布时间】:2020-05-27 11:54:04 【问题描述】:为什么无论整数数组对象的定义中指定的值如何,这段代码都会导致数组的第二个元素打印为 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;
【问题讨论】:
user4581301 告诉你原因 - 你的构造函数有未定义的行为。但附带说明一下,不要调用exit()
进行越界访问,而是抛出异常,例如std::out_of_range
。您还应该处理index
小于0 的情况。此外,由于您正在实现构造函数和析构函数,因此您还应该根据Rule of 3/5/0 实现赋值运算符。在这种情况下,你真的应该改用std::vector
。
如何抛出 std::out_of_range 异常而不收到“Aborted (core dumped)”消息。
catch
退出前的异常main()
【参考方案1】:
在Array
构造函数中,在ptr
处分配和填充动态分配的缓冲区后,立即释放缓冲区
delete ptr;
在此之后对ptr
缓冲区的所有访问都会调用undefined behaviour。旁注:这应该是 delete[] ptr;
以确保正确释放数组。
解决方案:不要那样做!
当Array
超出范围并使用缓冲区时,添加一个析构函数以释放ptr
。
// destructor for array class
Array::~Array()
delete[] ptr;
编译器会自动为您生成一个析构函数,但该泛型析构函数没有资格知道delete[]
指针成员处的内容是否安全。它可能不是一个数组,分配可能由另一个对象拥有(参见What is ownership of resources or pointers?)或者可能不是使用new
动态分配的。
这带来了一个旁注:处理复制此对象的默认特殊成员函数将盲目地复制指针,而不是分配,并留下两个指向相同分配的对象。迟早这将是致命的,因为一个副本将在另一个之前超出范围,如果没有其他尝试访问已释放的分配并破坏程序,第二个delete[]
将破坏程序。 What is The Rule of Three?
一般的经验法则是不要创建这样的类,而是使用std::vector
。 std::vector
完成了所有这些以及更多工作。
【讨论】:
不仅如此,OP 还使用delete
而不是delete[]
,因此是未定义行为的次要原因。
是否需要显式定义析构方法?删除“delete ptr;”行就足够了,对吗?
@DarnocEloc 构造函数将为您生成一个简单的析构函数,但这个简单的析构函数不会释放内存,因为在很多情况下您不想释放指针处的内容。一方面,它可能不是动态分配的,也可能不是要删除的对象的分配。【参考方案2】:
我将代码修改为包含显式默认构造函数和复制构造函数,还包含 std::out_of_range 异常,但不确定后者是否正确实现。 这是在不使用 STL 的向量容器的情况下处理数组的练习。 添加了交换成员函数和赋值运算符,但收到一些错误消息。
类“Array”没有成员“swap” 成员“Array::size”(在第 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;
【讨论】:
你的operator[]
不应该是catch
'ing 例外它throw
s。这违背了引发异常的全部目的,它允许使用无效的 index
调用 return ptr[index];
。
我用一个 throw out of range 语句替换了 try catch 块。
复制构造函数看起来不错。强烈建议使用operator=
来填写分配发生时会遇到的最后一个潜在错误。 Copy and Swap Idiom 提供了一种简单有效的方法来使用复制构造函数为 =
完成繁重的工作。
新想法:一旦你对你的数组类感到满意,如果你有时间,看看通过Code Review 运行它会出现什么结果。带上厚脸皮。审阅者很有礼貌,但是无论人们对它有多好,人们都会梳理你的代码并找到可以更好的东西总是令人讨厌。我已经链接到帮助页面,因为一个广受欢迎的问题的代码要求必然是相当具体的。【参考方案3】:
修改了打印函数以排除最后一个数组元素后的空格。 修改构造函数方法声明以包含初始化参数。 添加了额外的 const 版本的 index[ ] 运算符重载,但认为它没有正确实现或者是否会实际使用。
#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;
【讨论】:
以上是关于数组索引重载错误的主要内容,如果未能解决你的问题,请参考以下文章
Swift 3 错误:“参数标签 '(named:_:,_:,)' 不匹配任何可用的重载”,同时尝试将 UIImage 数组加载到 UITableView