我现在正在设计自己的带有邻接列表的图形类。除了析构函数之外,我完成了大部分步骤。
这是我的Vertex课程:
struct Vertex{
public:
Vertex(){m_name="";}
Vertex(string name):m_name(name){}
~Vertex(){
cout << "vertex des" << endl;
for(int i = 0; i < m_edge.size(); i++){
delete m_edge[i];
m_edge[i] = nullptr;
}
}
string m_name;
vector<Edge*> m_edge;
};
这是我的Edge类:
struct Edge{
public:
Edge() : m_head(nullptr), m_tail(nullptr) {m_name="";}
Edge(string name) : m_name(name), m_head(nullptr), m_tail(nullptr) {}
~Edge(){
cout << "Edge des" << endl;
delete m_head;
m_head = nullptr;
delete m_tail;
m_tail = nullptr;
}
string m_name;
Vertex* m_head;
Vertex* m_tail;
};
但是,我注意到在调用析构函数时,两个类实际上都调用了彼此的析构函数,所以这给了我一个无限循环。这个设计有问题吗?如果没有,有没有办法解决这个析构函数问题?谢谢!
答案 0 :(得分:4)
然而,我注意到在调用析构函数时,两个类实际上都会相互调用析构函数,所以这给了我一个无限循环。这个设计有问题吗?
您当前的设计确实存在问题。动态分配应仅由其各自的所有者删除。通常,所有者是创建对象的人,通常只有一个所有者。如果有多个对象,则所有权为 shared 。共享所有权需要一种机制 - 例如引用计数 - 来跟踪当前的所有者数量。
根据目标来判断,你的顶点似乎是拥有的&#34;通过多个边缘,边缘看起来由多个顶点拥有。如果不是这样,那么你的图表会很无聊。但您尚未实施任何形式的所有权跟踪。
我建议使用更简单的设计,其中边缘不具有顶点,顶点也不具有边缘。它们都应该由父对象拥有,可能称为Graph
。
答案 1 :(得分:2)
由于问题标记为C ++ 11,因此应首先使用托管指针。在托管指针中,weak_ptr可以帮助您打破循环依赖:
#include <vector>
#include <memory>
#include <string>
#include <iostream>
using namespace std;
struct Edge;
struct Vertex{
public:
Vertex(){m_name="";}
Vertex(string name):m_name(name){}
~Vertex(){
cout << "vertex des" << endl;
for(auto e : m_edge)
{
if(e->m_head.lock().get() == this)
{
e->m_head = nullptr;
}
if(e->m_tail.lock().get() == this)
{
e->m_tail = nullptr;
}
}
string m_name;
vector<shared_ptr<Edge>> m_edge;
};
这里你的原始指针已被更改为shared_ptr
s:无需在销毁时调用delete,但是你应该告诉边缘忘记顶点(参见下面的head and tail声明)。
struct Edge{
public:
Edge(){m_name="";}
Edge(string name):m_name(name){}
~Edge(){
cout << "Edge des" << endl;
// if you're here, this means no vertices points to the edge any more:
// no need to inform head or tail the edge is destroyed
}
void do_something_to_head()
{
auto head = m_head.lock();
if(head)
{
head->do_something();
}
}
string m_name;
weak_ptr<Vertex> m_head;
weak_ptr<Vertex> m_tail;
};
weak_ptr
更改了边缘中的原始指针:它们是&#34;非拥有&#34;指向共享资源的对象。您无法直接取消引用weak_ptr
:您必须调用方法lock
,如果它存在,则为指向的资源创建临时shared_ptr
(从而防止循环依赖)。用法:
int main()
{
shared_ptr<Vertex> v1 = make_shared<Vertex>();
shared_ptr<Vertex> v2 = make_shared<Vertex>();
// connection should be encapsulated somewhere
shared_ptr<Edge> e = make_shared<Edge>();
v1->m_edge.push_back(e);
e->m_head = v1;
v2->m_edge.push_back(e);
e->m_tail = v2;
return 0;
}
答案 2 :(得分:1)
我认为,这是一个设计问题,因为在图形术语中 - 当您删除Edge时 - 您不应该删除其顶点。
我想,
m_head = nullptr;
m_tail = nullptr;
就足够了。
答案 3 :(得分:1)
与其他人一样,对你的问题的简短回答是:是的,调用everyover的析构函数是有问题的,因为这可能会导致未定义的行为。
例如,看看这种情况:
Vertex
对象v
,Edge
中的第一个m_edge
,m_head
和m_tail
Vertex
,v
,那么v
的析构函数中的下一个循环将尝试访问已删除的数据!!! 充其量,您的程序将会出现段错误;最坏的......谁知道......
你的设计并不坏。然而,它的问题是你无法清楚地定义所有权(这可能有助于知道谁应该摧毁谁)。
确实,假设Vertex
可以与多个(和至少一个)Edge
相关,并且Edge
与恰好两个Vertex
有关,那么您可以认为Edge
由一对Vertex
拥有。在那种情况下,管理删除顺序并不容易......
然而,你并不一定需要一个所有权关系来指出谁应该摧毁谁。如上所述,Edge
恰好与两个Vertex
相关;如果其中一个被销毁,那么Edge
也应该被销毁。另一方面,如果Edge
被销毁,则没有理由销毁与其相关的任何Vertex
,因为每个Edge
仍可能与其他现有Vertex
相关;唯一的例外是当Edge
与任何struct Edge;
struct Vertex {
public:
// ctors unchanged
~Vertex(); // implemented below
void remove_relation(Edge* edge) // for use by Edge only
{
std::vector<Edge*>::iterator it =
std::find(m_edge.begin(), m_edge.end(), edge);
if (it != m_edge.end())
m_edge.erase(it);
if (m_edge.size() == 0)
delete this; // this Vertex can be safely deleted
}
string m_name;
vector<Edge*> m_edge;
};
struct Edge {
public:
// ctors unchanged
~Edge() {
// Only have to remove relation with m_head & m_tail
if (m_head)
m_head->remove_relation(this);
if (m_tail)
m_tail->remove_relation(this);
std::cout << "deleted Edge " << this << std::endl;
}
void delete_from_vertex(Vertex* from) // for use by Vertex only
{
// Prevent from removing relation with the calling Vertex
if (m_head == from)
m_head = nullptr;
else if (m_tail == from)
m_tail = nullptr;
else
assert(false); // Vertex not in relation with this Edge
delete this; // finally destroy this Edge
}
string m_name;
Vertex* m_head;
Vertex* m_tail;
};
Vertex::~Vertex() {
for(int i = 0; i < m_edge.size(); i++)
m_edge[i]->delete_from_vertex(this); // special destruction
std::cout << "deleted Vertex " << this << std::endl;
}
没有更多关系时。遵循这些规则的代码如下:
folder