数组索引重载错误

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::vectorstd::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 例外它throws。这违背了引发异常的全部目的,它允许使用无效的 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

Powershell如何重载数组索引运算符?

使用 jax 数组索引到 numpy 数组:错误的错误消息

Swift 致命错误:数组索引超出范围

Swift 致命错误:数组索引超出范围

访问数组索引时如何强制打字稿给出错误