// 在C++中,实现有向图的一种常见方式是使用邻接表。
// 邻接表的核心思想是,对于图中的每个节点,使用一个"链表"来存储它的邻接节点。每个链表中的节点表示从当前节点出发的边的终点。
// 下面是一个简单的实现示例,其中包含以下组件:
// Graph: 表示整个有向图。
// Node: 表示一个节点及其连接的邻接表。
// AdjNode: 表示邻接表中的一个节点。
// 在这个代码示例中,Graph类表示一个有向图。它使用了邻接表来存储每个节点的邻接边。
// 每个节点的邻接表是一个链表,使用 std::shared_ptr 来确保动态分配的内存得到妥善管理。
// addEdge 方法用于向图中添加有向边,
// printGraph 方法用于打印整个图的结构。
// 这段代码涉及到有向图中使用邻接表实现添加边的操作。
// 我们来逐句解释代码的功能:
// auto newNode = std::make_shared<AdjNode>(destination);:
// 这行代码使用std::make_shared创建一个新的AdjNode(邻接节点)对象。AdjNode的构造函数被调用,并传入了目标节点(destination)作为参数。
// std::make_shared返回一个智能指针(std::shared_ptr),指向新创建的AdjNode对象。这种智能指针负责自动管理内存,确保对象在不再需要时正确释放。
// newNode->next = nodes[source].head;:
// 这行代码将新创建的邻接节点的next指针指向源节点的当前邻接表头部。
// 这样做的目的是将新创建的节点插入到邻接表的头部,即成为链表中的第一个节点。原有的链表头部节点成为新创建节点的下一个节点。
// nodes[source].head = newNode;:
// 这行代码将源节点的邻接表头部更新为新创建的邻接节点。
// 这样做的目的是将新创建的节点设为链表的头节点,从而确保新节点成为邻接表中的第一个节点。这一行与前一行代码共同实现了将新节点插入邻接表头部的操作。
// 综上所述,这段代码实现了向图的源节点的邻接表中添加一个新的邻接节点,并将其插入到邻接表的头部位置。这是向有向图中添加边的基本操作之一。
#include <iostream>
#include <vector>
#include <memory>
// 表示邻接表中的一个节点
struct AdjNode {
int dest; // 目的节点
std::shared_ptr<AdjNode> next; // 指向下一个邻接节点的指针
AdjNode(int d) : dest(d), next(nullptr) {}
};
// 表示图中的一个节点
struct Node {
std::shared_ptr<AdjNode> head; // 指向邻接表头部的指针
Node() : head(nullptr) {}
};
// 表示整个有向图
class Graph {
public:
// 初始化图,给定节点数
Graph(int n) : numVertices(n), nodes(n) {}
// 添加有向边,从源节点到目标节点
void addEdge(int source, int destination) {
auto newNode = std::make_shared<AdjNode>(destination);
newNode->next = nodes[source].head;
nodes[source].head = newNode;
}
// 打印整个图
void printGraph() {
for (int i = 0; i < numVertices; ++i) {
std::cout << "Node " << i << ":";
auto current = nodes[i].head;
while (current) {
std::cout << " -> " << current->dest;
current = current->next;
}
std::cout << std::endl;
}
}
private:
int numVertices; // 图中节点数
std::vector<Node> nodes; // 存储所有节点
};
int main() {
Graph g(5); // 创建一个有5个节点的图
// 添加一些有向边
g.addEdge(0, 1);
g.addEdge(0, 4);
g.addEdge(1, 2);
g.addEdge(1, 3);
g.addEdge(1, 4);
g.addEdge(3, 4);
// 打印整个图
g.printGraph();
return 0;
}
// 在C++中,实现有向图的一种常见方式是使用邻接表。
// 邻接表的核心思想是,对于图中的每个节点,使用一个"链表"来存储它的邻接节点。每个链表中的节点表示从当前节点出发的边的终点。
// 下面是一个简单的实现示例,其中包含以下组件:
// Graph: 表示整个有向图。
// Node: 表示一个节点及其连接的邻接表。
// AdjNode: 表示邻接表中的一个节点。
// 在这个代码示例中,Graph类表示一个有向图。它使用了邻接表来存储每个节点的邻接边。
// 每个节点的邻接表是一个链表,使用 std::shared_ptr 来确保动态分配的内存得到妥善管理。
// addEdge 方法用于向图中添加有向边,
// printGraph 方法用于打印整个图的结构。
// 这段代码涉及到有向图中使用邻接表实现添加边的操作。
// 我们来逐句解释代码的功能:
// auto newNode = std::make_shared<AdjNode>(destination);:
// 这行代码使用std::make_shared创建一个新的AdjNode(邻接节点)对象。AdjNode的构造函数被调用,并传入了目标节点(destination)作为参数。
// std::make_shared返回一个智能指针(std::shared_ptr),指向新创建的AdjNode对象。这种智能指针负责自动管理内存,确保对象在不再需要时正确释放。
// newNode->next = nodes[source].head;:
// 这行代码将新创建的邻接节点的next指针指向源节点的当前邻接表头部。
// 这样做的目的是将新创建的节点插入到邻接表的头部,即成为链表中的第一个节点。原有的链表头部节点成为新创建节点的下一个节点。
// nodes[source].head = newNode;:
// 这行代码将源节点的邻接表头部更新为新创建的邻接节点。
// 这样做的目的是将新创建的节点设为链表的头节点,从而确保新节点成为邻接表中的第一个节点。这一行与前一行代码共同实现了将新节点插入邻接表头部的操作。
// 综上所述,这段代码实现了向图的源节点的邻接表中添加一个新的邻接节点,并将其插入到邻接表的头部位置。这是向有向图中添加边的基本操作之一。
--
智能指针 shared_ptr 是 C++ 标准库提供的一种智能指针类型,用于管理动态分配的资源,特别是在动态内存分配和释放方面非常有用。它可以确保在没有引用时释放资源,避免内存泄漏,并能够安全地共享资源的所有权。
std::shared_ptr
是 C++11 引入的智能指针类型之一,旨在提供一种自动化的内存管理方式。与原生指针相比,它具有自动管理内存的优势,可以避免内存泄漏和重复释放等问题。
下面是 shared_ptr 的一些关键特点:
1. 自动内存管理: shared_ptr 可以自动管理动态分配的内存资源的生命周期。当没有指向资源的 shared_ptr 时,资源会被自动释放,无需手动调用 delete。
2. 引用计数: shared_ptr 通过引用计数来跟踪资源的使用情况。每当创建一个指向资源的 shared_ptr,计数器就会增加;当 shared_ptr 被销毁时,计数器减少。当计数器为零时,资源会被释放。
3. 多个指针共享资源: 多个 shared_ptr 可以指向同一块内存资源,它们共享对资源的访问和所有权。这使得在程序中传递和共享资源变得更加简单和安全。
4. 线程安全: shared_ptr 的引用计数是线程安全的,因此可以在多线程环境中安全地使用。
5. 循环引用解决方案: shared_ptr 能够检测并解决循环引用的问题,当存在循环引用时,通过使用 weak_ptr 来打破循环引用,防止内存泄漏。
总之,智能指针 shared_ptr 提供了一种方便而安全的方式来管理动态分配的资源,它是现代 C++ 编程中的重要工具之一。
#include <iostream>
#include <memory> // 引入 shared_ptr 头文件
int main() {
// 创建一个 shared_ptr 指向一个新的 int 对象,并初始化值为 42
std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
// 打印共享指针指向的值
std::cout << "Value: " << *ptr1 << std::endl;
// 打印当前引用计数(应该是 1,因为只有 ptr1 引用该对象)
std::cout << "Reference count: " << ptr1.use_count() << std::endl;
{
// 在一个新的作用域中创建另一个 shared_ptr
std::shared_ptr<int> ptr2 = ptr1; // ptr2 和 ptr1 共享同一个 int 对象
// 打印新的引用计数(应该是 2,因为 ptr1 和 ptr2 都引用同一个对象)
std::cout << "Reference count: " << ptr1.use_count() << std::endl;
// ptr2 离开作用域时,会自动释放对对象的引用,但对象不会被销毁,因为 ptr1 仍然持有该对象
}
// 在作用域结束后,打印引用计数(应该是 1,因为 ptr2 已被销毁)
std::cout << "Reference count after scope: " << ptr1.use_count() << std::endl;
// 当 ptr1 离开作用域时,内存将自动释放
return 0;
}
在这个例子中:
-
我们首先创建了一个
shared_ptr
,并使用std::make_shared
创建了一个新的整数对象并初始化值为 42。 -
然后我们打印了当前
shared_ptr
的引用计数和指向的值。 -
接着,我们在一个新的作用域中创建了另一个
shared_ptr
指向相同的整数对象。此时,引用计数变为 2,因为有两个指针引用同一个对象。 -
当作用域结束后,
ptr2
离开作用域,被销毁,引用计数又变回 1。 -
最后,当
ptr1
也离开作用域时,整数对象将被销毁,因为没有其他指针引用它。
这个例子展示了 std::shared_ptr
的基本用法以及它如何自动管理内存。