推送到矢量矢量时出错

时间:2014-01-22 04:38:19

标签: c++ vector stl

我有一个指向矢量的矢量指针,这个矢量定义为

vector< vector<Edge*> >* adj;

并且已经启动,

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);
                ^

我不明白这有什么不对,我正确使用它吗?

2 个答案:

答案 0 :(得分:3)

adj有一个指针,所以你需要先取消引用它:

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

但话又说回来,你为什么要new并指出一切?你可以简单地做

// 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不需要取消引用。

答案 1 :(得分:1)

你需要一个二维数组的事实让你误入歧途:矢量中的矢量没有什么特别之处,它只是具有看起来二维的方便副作用。

首先让我们看一下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)的输出:

  

start:edges.capacity = 0 edges.size = 0

     

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

     

添加了5,0,5,5:edges.capacity = 2 edges.size = 2    edges [0] = {0,0,0,0}    edges [1] = {5,0,5,5}

     

预留5:edges.capacity = 5 edges.size = 2    edges [0] = {0,0,0,0}    edges [1] = {5,0,5,5}

     

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

     

调整大小为6:edges.capacity = 6 edges.size = 6    edges [0] = {0,0,0,0}    edges [1] = {5,0,5,5}    edges [2] = {5,5,0,5}    edges [3] = {0,0,0,0}    edges [4] = {0,0,0,0}    edges [5] = {0,0,0,0}

     

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

此时,您应该具备使用std :: vector的能力,但是对于您的特定用例,您需要记住std :: vector是通用的。你可以在其中包含另一个向量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< object-that-implements-operator [] >

当你写adj[12][7]实际发生的事情时,我们正在采用顶级对象adj(类型std :: vector&gt; , 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 operator`。编写它的另一种方法是:

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>尺寸为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个向量的向量。其中第一个现在包含一个Edge。我们可以将其称为:

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.

最后是跟踪特定边缘的问题。您的代码使用指向Edge *对象的指针。除非您更熟悉指针,否则 会导致内存泄漏。将指针放入向量中使其成为指针的所有者。如果您不希望应用程序内存不足,则负责将内存返回系统。您需要使用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] = '\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。 / p>

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),它返回对内部向量的引用,然后我们在其上调用方法operator[](1)