在C ++中手动调整数组大小

时间:2018-11-04 08:23:44

标签: c++ c++11

很抱歉,如果以前已经解决过这个问题。我知道如何使用C和Java而不是C ++来做到这一点。在不使用包含Vector的现有类的情况下,给定以下代码,如何增加数组的大小?

数组扩展和对数组的分配在带有大写注释的push()中进行。

编辑:正如我在下面的评论中提到的,这是一个有关手动重新分配数组而不是使用std :: vector或“动态数组”的问题。

Line.h

res = tf.zeros((3,19,19,12))
res[0][1][1][(2*4):(2*4 + 4)] = 1  
res[image][x][y][(i*4):(i*4+4)] = 1

Main.cpp

#include <iostream>
#include "Point.h"

using namespace std;

class Line {
    public:
        Line();
        virtual ~Line();

        // TAKE IN NEW POINT, INCREASE THE ARRAY SIZE AND ADD NEW POINT TO THE END OF THE ARRAY
        void push(const Point& p);

    private:
        unsigned int index;  // size of "points" array
        Point* points;

};

4 个答案:

答案 0 :(得分:4)

简单的答案是,在这种情况下,您应始终使用std :: vector。但是,解释为什么会这样可能很有用。因此,让我们考虑一下如何在没有std :: vector的情况下实现此目标,以便您了解为什么要使用std :: vector:

// Naive approach
Line::push(const Point& p)
{
    Point* new_points = new Points[index + 1];
    std::copy(std::make_move_iterator(points), std::make_move_iterator(points+index), new_points);
    new_points[index] = p;
    delete[] points;
    points = new_points;
    index += 1;
}

这种方法有很多问题。每次插入条目时,我们都被迫重新分配并移动整个阵列。但是,矢量将预先分配一个预留空间,并为每个插入使用预留空间之外的空间,仅在超出预留空间限制时才重新分配空间。这意味着矢量将在性能方面远远超出您的代码执行能力,因为不必要地花费了更少的时间分配和移动数据。接下来是异常问题,此实现没有异常保证,而std :: vector为您提供了强大的异常保证:https://en.wikipedia.org/wiki/Exception_safety。为您的类实现强大的异常保证绝非易事,但是如果您按照这样的std :: vector来实现,则您将自动获得此保证

Line::push(const Point& p)
{
    points.push_back(p);
}

您的方法还有其他更细微的问题,您的类未定义复制或赋值运算符,因此会生成由编译器生成的浅表复制版本,这意味着如果有人复制您的类,则分配的成员将被删除两次。要解决此问题,您需要遵循C ++ 11之前的3范式规则和C ++ 11以后的5规则:https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)。但是,如果您使用了向量,则不需要这一切,因为您将从零规则中受益,并且能够依赖编译器生成的默认值:https://blog.rmf.io/cxx11/rule-of-zero

答案 1 :(得分:3)

基本上,唯一的方法是使用动态数组(使用new[]创建的动态数组)并创建全新的动态数组并复制(或移动) )从旧的 array 到新的对象。

类似这样的东西:

class Line {

public:
    Line(): index(0), points(nullptr) {} // initialize
    virtual ~Line() { delete[] points; } // Clean up!

    void push(const Point& p)
    {
        // create new array one element larger than before
        auto new_points = new Point[index + 1];

        // copy old elements to new array (if any)
        for(unsigned int p = 0; p < index; ++p)
            new_points[p] = points[p];

        new_points[index] = p; // then add our new Point to the end

        ++index; // increase the recorded number of elements

        delete[] points; // out with the old

        points = new_points; // in with the new


    }

private:
    unsigned int index;  // size of "points" array
    Point* points;

};

但是这种方法效率很低。做好这项工作非常复杂。用这种方式做事的主要问题是:

  • 异常安全性-避免在引发异常时发生内存泄漏。
  • 分配-避免每次都必须重新分配(和重新复制)。
  • 移动语义-相对于复制,充分利用某些对象移动 的能力。

更好的版本:

class Line {

public:
    Line(): index(0) {} // initialize
    virtual ~Line() { } // No need to clean up because of `std::unique_ptr`

    void push(const Point& p)
    {
        // create new array one element larger than before
        auto new_points = std::unique_ptr<Point[]>(new Point[index + 1]);    

        // first add our new Point to the end (in case of an exception)
        new_points[index] = p; 

        // then copy/move old elements to new array (if any)
        for(unsigned int p = 0; p < index; ++p)
            new_points[p] = std::move(points[p]); // try to move else copy

        ++index; // increase the recorded number of elements

        std::swap(points, new_points); // swap the pointers
    }

private:
    unsigned int index;  // size of "points" array
    std::unique_ptr<Point[]> points; // Exception safer

};

这要照顾到例外安全性和(一定程度上但不是全部)移动语义。但是必须指出,如果存储在数组(类型Point)中的元素本身是 exception,则 exception safety 仅将是 complete 复制移动时安全

但是,这不涉及有效分配。 std::vector过度分配,因此它不必对每个新元素都这样做。这段代码还遗漏了std::vector会采用的其他技巧(例如分配未初始化内存,以及在需要/丢弃时手动构造/破坏元素)。

答案 2 :(得分:2)

除了分配一个新数组,复制现有值并delete []旧值之外,基本上别无选择。这就是向量通过乘数因子进行重新分配的原因(例如,每个重新分配的大小都会增加一倍)。这是您要使用标准库结构而不是重新实现的原因之一。

答案 3 :(得分:0)

保持简单

在我看来,在这种情况下,最好在CPoint中使用CLine的链表:

struct CPoint
{
    int x = 0, y = 0;
    CPoint * m_next = nullptr;
};


class CLine
{
public:
    CLine() {};
    virtual ~CLine()
    {
        // Free Linked-List:
        while (m_points != nullptr) {
            m_current = m_points->m_next;
            delete m_points;
            m_points = m_current;
        }
    };

    // TAKE IN NEW POINT, INCREASE THE ARRAY SIZE AND ADD NEW POINT TO THE END OF THE ARRAY
    void push(const CPoint& p)
    {
        m_current = (((m_points == nullptr) ? (m_points) : (m_current->m_next)) = new CPoint);
        m_current->m_x = p.m_x;
        m_current->m_y = p.m_y;
        m_index++;
    };

private:
    unsigned int m_index = 0;  // size of "points" array
    CPoint * m_points = nullptr, * m_current = nullptr;
};

或者,使用智能指针甚至更好:

#include <memory>

struct CPoint
{
    int m_x = 0, m_y = 0;
    std::shared_ptr<CPoint> m_next;
};


class CLine
{
public:
    CLine() {};
    virtual ~CLine() {}

    // TAKE IN NEW POINT, INCREASE THE ARRAY SIZE AND ADD NEW POINT TO THE END OF THE ARRAY
    void push(const CPoint& p)
    {
        m_current = (((m_points == nullptr) ? (m_points) : (m_current->m_next)) = std::make_shared<CPoint>());
        m_current->m_x = p.m_x;
        m_current->m_y = p.m_y;
        m_index++;
    };

private:
    unsigned int m_index = 0;  // size of "points" array
    std::shared_ptr<CPoint> m_points, m_current;
};