现代 C++ 对象关系
Modern C++ Object Relationships
我有一个使用 struct Node
和 struct Edge
实现的图表,其中:
- 每个
Edge
都有开始和结束Node
- 每个
Node
维护一个 Edge
对象的列表,这些对象从它开始或结束
以下是一种可能的实现方式:
struct Node;
struct Edge {
Node *st;
Node *en;
int some_data;
};
const int MAX_EDGES = 100;
struct Node {
Edge *edges[MAX_EDGES];
int some_data;
};
虽然上面的结构可以表示我想要的图形,但我想在满足以下要求的情况下以 "Modern C++" 的方式进行:
- 避免指针
- 对
Node::edges
使用 std::vector
- 能够在标准 C++ 容器中存储
Node
和 Edge
对象
这在现代 C++ 中是如何完成的? 1-3都能实现吗?
- 通过使用 std::shared_ptr 或 std::unique_ptr
- 我不认为向量在这里是一个正确的选择,因为图形通常不是线性的(通常来说,在大多数情况下,你不能像堆一样线性化它)
- 没有标准的 'general-use' 容器,但您可以在此处使用模板以实现通用性
例如,您的元素 class 可以如下所示:
template <class T>
struct Elem {
std::shared_ptr<Node> st , en;
T some_data;
};
说到现代 C++,我认为这里不鼓励结构,你应该封装你的数据
- Avoid pointers
您可以为此使用 std::shared_ptr
和 std::weak_ptr
。只需决定您是否希望节点拥有边,反之亦然。非拥有类型应该使用weak_ptr
(以避免循环)。
除非您的图表是非循环的,否则您可能仍需要注意所有权循环。
std::unique_ptr
不是一个选项,因为节点和边之间没有一对一的关系,所以任何给定对象都不可能有唯一的所有者。
- Use an std::vector for Node::edges
没问题。使其成为 std::vector<std::weak_ptr<Edge>>
或 std::vector<std::shared_ptr<Edge>>
(取决于边是否拥有节点,反之亦然)
- Be able to store Node and Edge objects in standard C++ containers
没问题,只需确保您的类型可以安全 moved/copied 而不会泄漏或损坏内存,即具有正确的 copy/move 构造函数和赋值运算符。如果您按照上面的建议使用智能指针和 std::vector
,那将自动发生。
现代 C++ 避免将动态内存分配给原始指针。这是因为很容易忘记删除所述指针。话虽如此,使用原始指针作为对对象的引用并没有错,前提是您可以保证对象的生命周期将大于使用所述指针。
规则一般是:
- 如果对象只有一个所有者,则使用
std::unique_ptr
。
- 使用原始指针来引用在 1. 中创建的对象,前提是您可以保证对象的生命周期长于引用的使用。
- 对引用计数对象使用
std::shared_ptr
- 当你不想增加引用计数时,使用
std::weak_ptr
引用一个引用计数对象。
因此,在您的情况下,如果 Edge
拥有 Node
,则使用 std::unique_ptr
,否则,请保留原始指针。
在你的 Node
class 中,如果 Node
拥有 Edges
使用 std::vector<Edge>
否则使用 std::vector<Edge*>
尽管它可能更有效 link 你的 Edges
在他们自己的侵入性 linked 列表中。
在复杂图形上完成了一些工作后,可能会将所有 Node
s 和 Edge
es 分配到图形外部的向量中,然后仅在内部使用原始指针在内部引用它们图形。记住内存分配很慢,所以你做的越少,你的算法就会越快。
我有一个使用 struct Node
和 struct Edge
实现的图表,其中:
- 每个
Edge
都有开始和结束Node
- 每个
Node
维护一个Edge
对象的列表,这些对象从它开始或结束
以下是一种可能的实现方式:
struct Node;
struct Edge {
Node *st;
Node *en;
int some_data;
};
const int MAX_EDGES = 100;
struct Node {
Edge *edges[MAX_EDGES];
int some_data;
};
虽然上面的结构可以表示我想要的图形,但我想在满足以下要求的情况下以 "Modern C++" 的方式进行:
- 避免指针
- 对
Node::edges
使用 - 能够在标准 C++ 容器中存储
Node
和Edge
对象
std::vector
这在现代 C++ 中是如何完成的? 1-3都能实现吗?
- 通过使用 std::shared_ptr 或 std::unique_ptr
- 我不认为向量在这里是一个正确的选择,因为图形通常不是线性的(通常来说,在大多数情况下,你不能像堆一样线性化它)
- 没有标准的 'general-use' 容器,但您可以在此处使用模板以实现通用性
例如,您的元素 class 可以如下所示:
template <class T>
struct Elem {
std::shared_ptr<Node> st , en;
T some_data;
};
说到现代 C++,我认为这里不鼓励结构,你应该封装你的数据
- Avoid pointers
您可以为此使用 std::shared_ptr
和 std::weak_ptr
。只需决定您是否希望节点拥有边,反之亦然。非拥有类型应该使用weak_ptr
(以避免循环)。
除非您的图表是非循环的,否则您可能仍需要注意所有权循环。
std::unique_ptr
不是一个选项,因为节点和边之间没有一对一的关系,所以任何给定对象都不可能有唯一的所有者。
- Use an std::vector for Node::edges
没问题。使其成为 std::vector<std::weak_ptr<Edge>>
或 std::vector<std::shared_ptr<Edge>>
(取决于边是否拥有节点,反之亦然)
- Be able to store Node and Edge objects in standard C++ containers
没问题,只需确保您的类型可以安全 moved/copied 而不会泄漏或损坏内存,即具有正确的 copy/move 构造函数和赋值运算符。如果您按照上面的建议使用智能指针和 std::vector
,那将自动发生。
现代 C++ 避免将动态内存分配给原始指针。这是因为很容易忘记删除所述指针。话虽如此,使用原始指针作为对对象的引用并没有错,前提是您可以保证对象的生命周期将大于使用所述指针。
规则一般是:
- 如果对象只有一个所有者,则使用
std::unique_ptr
。 - 使用原始指针来引用在 1. 中创建的对象,前提是您可以保证对象的生命周期长于引用的使用。
- 对引用计数对象使用
std::shared_ptr
- 当你不想增加引用计数时,使用
std::weak_ptr
引用一个引用计数对象。
因此,在您的情况下,如果 Edge
拥有 Node
,则使用 std::unique_ptr
,否则,请保留原始指针。
在你的 Node
class 中,如果 Node
拥有 Edges
使用 std::vector<Edge>
否则使用 std::vector<Edge*>
尽管它可能更有效 link 你的 Edges
在他们自己的侵入性 linked 列表中。
在复杂图形上完成了一些工作后,可能会将所有 Node
s 和 Edge
es 分配到图形外部的向量中,然后仅在内部使用原始指针在内部引用它们图形。记住内存分配很慢,所以你做的越少,你的算法就会越快。