推向向量向量时的错误

Error when pushing to vector of vectors

本文关键字:向量 错误      更新时间:2023-10-16

我有一个指向向量向量的指针,

vector< vector<Edge*> >* adj;

和initilized,

adj = new vector< vector<Edge*> >(v, vector<Edge*>());

当我推回这个向量的向量时,

Edge* e = new Edge(v, w);
adj[u].push_back(e);

我有一个错误,

prog.cpp:27:20: error: no matching function for call to ‘std::vector<std::vector<Edge*> >::push_back(Edge*&)’
  adj[u].push_back(e);
                ^

我不明白这是怎么回事,我正确地使用了吗?第一:

  Edge* e = new Edge(v, w);
  (*adj)[u].push_back(e);
// ^

但是,再说一次,您为什么要 new in and tockify and operify othert?您可以简单地做

// Create v amounts of vector<Edge>s
vector< vector<Edge> > adj(v, vector<Edge>());
Edge e(v, w);
adj[u].push_back(e);   // assuming u is a valid index

它更容易阅读,更少的错误容易出现(您不必手动delete所有内容),而且我确定比旧版本更具性能。

更新:哦,e不需要被删除。

您需要一个二维阵列的事实使您误入歧途:对向量内的向量没有什么特别的,它只是具有二维的方便副作用。

所以让我们首先看一下std::vector<int>

#include <vector>
#include <iostream>
class Point {
    int m_x, m_y;
public:
    Point() : m_x(0), m_y(0) {}
    Point(int x, int y) : m_x(x), m_y(y) {}
    int X() const { return m_x; }
    int Y() const { return m_y; }
};
class Edge {
    Point m_start, m_end;
public:
    Edge() : m_start(), m_end() {}
    Edge(const Point& start, const Point& end)
        : m_start(start)
        , m_end(end)
    {}
    Edge(int startX, int startY, int endX, int endY)
        : m_start(startX, startY)
        , m_end(endX, endY)
    {}
    const Point& Start() const { return m_start; }
    const Point& End() const { return m_end; }
};
void dumpEdges(const char* label, const std::vector<Edge>& edges)
{
    std::cout << label << ":"
              << " edges.capacity = " << edges.capacity()
              << " edges.size = " << edges.size()
              << 'n';
    for (size_t i = 0, end = edges.size(); i < end; ++i) {
        std::cout << " edges["<<i<<"] = { "
                  << edges[i].Start().X() << ", "
                  << edges[i].Start().Y() << ", " 
                  << edges[i].End().X() << ", "
                  << edges[i].End().Y() << " }n"
                     ;
    }
    std::cout << 'n';
}
int main()
{
    std::vector<Edge> edges;
    // this has created an empty, dimensionless vector on the stack.
    // when we push something to it, it will internally allocate memory
    // to store an Edge for us.
    dumpEdges("start", edges);
    edges.push_back(Edge(0, 0, 0, 0));
    dumpEdges("added 0,0,5,0", edges);
    // when we now push another Point onto this vector, it may find
    // it has used all the memory it allocated forcing it to allocate
    // more memory. When this happens, our previous "Point" object
    // will end up at a new address. We can avoid issues with this by
    // using index values rather than absolute addresses.
    edges.push_back(Edge(5, 0, 5, 5));
    dumpEdges("added 5,0,5,5", edges);
    // to save the program allocating memory every time we add a new
    // edge, we can predict how many we're going to need.
    edges.reserve(5);
    dumpEdges("reserve'd to 5", edges);
    // watch where the next push_back goes.
    edges.push_back(Edge(5, 5, 0, 5));
    dumpEdges("added 5,5,0,5", edges);
    // but when you use resize, you actually add empty elements:
    edges.resize(6);
    dumpEdges("resize'd to 6", edges);
    // watch where the next one goes.
    edges.push_back(Edge(0, 5, -5, 5));
    dumpEdges("added 0,5,-5,5", edges);
    return 0;
}

这是来自IDEONE.com(http://ideone.com/wp5rxr)的输出:

开始:edges.capacity = 0 edges.size = 0

添加0,0,5,0:edges.capacity = 1 edge.size = 1 边缘[0] = {0,0,0,0,0}

添加了5,0,5,5:edge.capacity = 2 edge.size = 2 边缘[0] = {0、0、0、0} 边缘[1] = {5,0,5,5}

储备至5:edge.capacity = 5 edge.size = 2 边缘[0] = {0、0、0、0} 边缘[1] = {5,0,5,5}

添加了5,5,0,5:edges.capacity = 5 edge.size = 3 边缘[0] = {0、0、0、0} 边缘[1] = {5,0,5,5} 边缘[2] = {5,5,0,5}

调整到6:edges.capacity = 6 edge.size = 6 边缘[0] = {0、0、0、0} 边缘[1] = {5,0,5,5} 边缘[2] = {5,5,0,5} 边缘[3] = {0,0,0,0} 边缘[4] = {0、0、0、0} 边缘[5] = {0,0,0,0,0}

添加0,5,-5,5:edges.capacity = 12 edges.size = 7 边缘[0] = {0、0、0、0} 边缘[1] = {5,0,5,5} 边缘[2] = {5,5,0,5} 边缘[3] = {0,0,0,0} 边缘[4] = {0、0、0、0} 边缘[5] = {0,0,0,0} 边缘[6] = {0,5,-5,5}

在这一点上,您应该拥有使用std :: vector的能力,但是对于特定用例,您需要记住std :: vector是通用的。您 can 将另一个向量包裹在其中, std::vector<std::vector<Edge>>。但这只是两个独立工作的向量。

void fn()
{
    std::vector<std::vector<Edge>> adj;

adj这是一个简单的std::vector,其中adj[n]解决std::vector<Edge>的实例。这两个向量并不真正了解彼此。adj[x][y]工作的事实是方便的魔术,它不是向量的特殊功能。它将适用于任何 std::vector<对象 - implements-operator [] >

编写adj[12][7]时,实际发生的事情是我们采用顶级对象,adj(键入std :: vector> , and calling it's member function操作员. This returns a reference to the 12th element of the top vector, type std :: vector , on which we then invoke运算符。>

adj . operator[](12) . operator[](7);

auto& object = adj;
auto& outerVectorElement = object.opterator[](12);
outerVectorElement.operator[](7);

此代码:

std::vector<std::vector<Edge>> adj(5);

创建一个std::vector<T>,并使用A size 的5。

std::vector<T> adj;
adj.resize(5);

现在,在上述情况下,T恰好是std::vector<Edge>。因此,它创建了5个向量的向量。

std::vector<std::vector<Edge>> adj(5, std::vector<Edge>());

这做类似但更昂贵的事情,实际上更像是这样:

std::vector<T> adj;
adj.reserve(5);
for (size_t i = 0; i < 5; ++i)
    adj.push_back(std::vector<Edge>());

因为那是"具有初始值的填充"的语法。它可能导致创建暂时的Edge对象。如果您想要的只是默认初始化,并且确定您需要固定的初始大小,只需使用

std::vector<T> adj(N);

更多代码:

std::vector<std::vector<Edge>> adj(initialSize);
std::vector<Edge>& firstAdjVec = adj[0];
firstAdjVec.push_back(Edge(1,2,3,4));

此时,adj是5个向量的向量。其中的第一个现在包含一个单个边缘。我们可以将其称为:

firstAdjVec[0];
adj[0][0];
(adj[0])[0];
auto& vec = adj[0]; vec[0];

这看起来像一个二维阵列,但事实并非如此。它是一个一维向量的数组,它们本身都有自己的向量数据,但它们是独立的。

如果您在C/C 中执行此操作:

Edge adj[10][12];

这将分配一个大的,连续的内存块,10 x 12。存储自己的元素。

这允许可变维度:

std::vector<std::vector<int>> grid;
grid.resize(10); // first dimension is now 0-10, all rows are 0 depth.
grid[0].push_back(1);
grid[2].push_back(2);
grid[2].push_back(3);
grid[0][0]; // returns 1;
grid[0][1]; // invalid, exceeds dimensions of std::vector& (grid[0]);
grid[1][0]; // invalid, std::vector& (grid[1]) is empty.
grid[2][1]; // valid
grid[3][0]; // invalid - empty.

最后是跟踪特定边缘的问题。您的代码使用指针来边缘*对象。除非您更熟悉指针,否则这是导致内存泄漏。将指针放入向量确实不是使其成为指针的所有者。如果您不希望应用程序用完存储器,则负责将内存返回系统。您需要为此使用delete

两个替代方案:一个,不要。只需删除指针并让您的向量管理内存即可。

std::vector<Edge>

瞧。不利的一面是,如果您想将这些边缘保留到其他地方,

void fn(Edge*);

当向量生长时,您将遇到问题:

#include <iostream>
#include <vector>
struct Foo {
    int m_i;
    char m_pad[1024]; // to make the object big.
    Foo(int i) : m_i(i) { m_name[0] = ''; }
};
int main()
{
    std::vector<Foo> foos;
    // grow the storage to support 4 Foos.
    foos.reserve(4);
    // emplace_back is like push_back but it passes
    // it's arguments directly to the constructor of
    // the new object, initializing it in-place, avoiding
    // a copy. so foo.emplace_back(1) will initialize
    // the new Foo by calling it's constructor with the value '1'.
    foo.emplace_back(1);
    std::cout << "First foo's address is " << (void)&foos[0] << 'n';
    // push 3 more.
    foo.emplace_back(2);
    foo.emplace_back(3);
    foo.emplace_back(4);
    std::cout << "First foo's address is " << (void)&foos[0] << 'n';
    // but now add lots more to force the storage to grow.
    foo.resize(64);
    std::cout << "First foo's address is now " << (void)&foos[0] << 'n';
    return 0;
}

要避免这种情况,只需使用索引而不是指针:

#include <vector>
#include <iostream>
struct Edge {
    Edge(int i=0) : m_i(i) {} // kill two birds with one constructor.
    int m_i;
};
typedef std::vector<std::vector<Edge>> EdgeGrid;
void pointerVersion(Edge* edge)
{
    std::cout << "pointing to " << (void*)edge << " -> " << edge->m_i << 'n';
}
void indexVersion(const EdgeGrid& adj, size_t x, size_y)
{
    std::cout << "adj["<<x<<"]["<<y<<"] = " << adj[x][y] << 'n';
}
int main()
{
    std::vector<std::vector<Edge>>> adj;
    adj.resize(4);
    adj[1].push_back(1);
    adj[1].push_back(2);
    adj[1].push_back(3);
    pointerVersion(&adj[1][2]);
    indexVersion(adj, 1, 2);
    return 0;
}

当然,在这种情况下,您不必总是将adj推到班级本身,那么您只需要告诉您要引用哪个x和y。

class EdgeContainer
{
    std::vector< std::vector< Edge > > m_adj;
public:
    EdgeContainer() : m_adj(10) {}
    // ...
};

更好的是,您可以通过给它来使其看起来像二维矢量

const std::vector<Edge>& operator[](size_t topIndex) const
{
    return m_adj[topIndex];
}

现在您可以做:

EdgeContainer adj;
// .. code to populate adj here
std::cout << "adj[3][1] = " << adj[3][1] << 'n';

记住,这实际上是在执行adj.operator[](3),该CC_26返回对内部向量的引用,然后我们在该vector上调用方法operator[](1)