我有一个指向矢量的矢量指针,这个矢量定义为
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);
^
我不明白这有什么不对,我正确使用它吗?
答案 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)
。