【C++风云录】深入了解STL、Boost.Graph和Google Test:发掘C++中的工具之力

本文主要是介绍【C++风云录】深入了解STL、Boost.Graph和Google Test:发掘C++中的工具之力,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

进阶高手之路:探索C++中的STL、Boost.Graph和Google Test绝技

前言

C++是一种强大的编程语言,为开发人员提供了广泛的工具和库。在这些工具中,STL(Standard Template Library)、Boost.Graph和Google Test被广泛应用于不同的领域。本文将深入探索这些工具,介绍它们的概述、使用方法和重要特性,帮助读者在C++开发中更加高效和准确地实现各种功能。

欢迎订阅专栏:C++风云录

文章目录

  • 进阶高手之路:探索C++中的STL、Boost.Graph和Google Test绝技
    • 前言
    • 1. STL(Standard Template Library)
      • 1.1 概述
      • 1.2 容器
        • 1.2.1 顺序容器
        • 1.2.2 关联容器
        • 1.2.3 容器适配器
      • 1.3 算法
        • 1.3.1 非修改性算法
        • 1.3.2 修改性算法
        • 1.3.3 排序和搜索算法
      • 1.4 迭代器
        • 1.4.1 输入迭代器
        • 1.4.2 输出迭代器
        • 1.4.3 前向迭代器
        • 1.4.4 双向迭代器
        • 1.4.5 随机访问迭代器
    • 2. Boost.Graph
      • 2.1 概述
      • 2.2 图的表示
        • 2.2.1 邻接矩阵
        • 2.2.2 邻接链表
      • 2.3 图的遍历
        • 2.3.1 深度优先搜索(DFS)
        • 2.3.2 广度优先搜索(BFS)
      • 2.4 最短路径算法
        • 2.4.1 Dijkstra算法
        • 2.4.2 Floyd-Warshall算法
      • 2.5 图的连通性
        • 2.5.1 连通分量
        • 2.5.2 强连通分量
      • 2.6 图的属性
        • 2.6.1 度
        • 2.6.2 密度
        • 2.6.3 直径
    • 3. Google Test
      • 3.1 概述
      • 3.2 单元测试
        • 3.2.1 基本断言
        • 3.2.2 参数化测试
      • 3.3 测试套件和测试夹具
        • 3.3.1 测试套件
        • 3.3.2 测试夹具
      • 3.4 测试覆盖率
        • 3.4.1 行覆盖率
        • 3.4.2 函数覆盖率
    • 总结

1. STL(Standard Template Library)

1.1 概述

STL(Standard Template Library)是C++标准库的一部分,它提供了一组通用的模板类和函数,用于实现常用的数据结构和算法。STL的设计目标是提供高效、可复用的组件,以便程序员能够更轻松地开发高质量的C++程序。

STL包括三个主要的组件:容器(Containers)、算法(Algorithms)和迭代器(Iterators)。容器提供了不同类型的数据存储方式,算法提供了常用的操作和算法实现,迭代器则用于访问和操作容器中的元素。

对于不同类型的问题,STL提供了多种容器和算法的选择,以便程序员根据需要选择合适的组件。STL的设计遵循了泛型编程的思想,使得代码能够更好地适应不同类型的数据。

下面是一个使用STL的示例代码,展示了如何使用vector容器和sort算法对一组整数进行排序:

#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> nums = {9, 5, 2, 7, 1};std::sort(nums.begin(), nums.end());for (int num : nums) {std::cout << num << " ";}std::cout << std::endl;return 0;
}

输出结果为:1 2 5 7 9,表示排序后的整数序列。

1.2 容器

STL提供了多种容器,用于存储和组织不同类型的数据。

1.2.1 顺序容器

顺序容器是按照元素插入的顺序进行存储的容器,其中包括vector、deque、list和array等。

  • vector:动态数组,支持快速随机访问和在末尾插入/删除元素。
  • deque:双端队列,支持在头部和尾部插入/删除元素。
  • list:双向链表,支持在任意位置插入/删除元素。
  • array:固定大小数组,支持快速随机访问。

下面是一个使用vector容器的示例代码,展示了如何向容器中添加元素,并遍历输出:

#include <iostream>
#include <vector>int main() {std::vector<int> nums;nums.push_back(1);nums.push_back(2);nums.push_back(3);for (int num : nums) {std::cout << num << " ";}std::cout << std::endl;return 0;
}

输出结果为:1 2 3,表示向vector容器中添加了三个整数,并遍历输出。

1.2.2 关联容器

关联容器是按照元素的键进行存储和访问的容器,其中包括set、map和unordered_map等。

  • set:有序集合,存储唯一的键值,并按照升序排列。
  • map:键值对的集合,按照键进行排序并存储唯一的键值。
  • unordered_map:键值对的集合,按照哈希值进行快速存储和访问。

下面是一个使用map容器的示例代码,展示了如何向容器中添加键值对,并根据键进行访问:

#include <iostream>
#include <map>int main() {std::map<std::string, int> scores;scores["Alice"] = 85;scores["Bob"] = 92;scores["Charlie"] = 78;std::cout << "Bob's score is " << scores["Bob"] << std::endl;return 0;
}

输出结果为:Bob’s score is 92,表示使用map容器存储了几个人的分数,并根据键"Bob"访问得到了对应的值。

1.2.3 容器适配器

容器适配器是在现有容器的基础上进行封装和操作的组件,包括stack、queue和priority_queue等。

  • stack:栈,后进先出的数据结构。
  • queue:队列,先进先出的数据结构。
  • priority_queue:优先队列,按照优先级进行排序的队列。

下面是一个使用stack容器的示例代码,展示了如何向栈中添加元素,并依次弹出并输出:

#include <iostream>
#include <stack>int main() {std::stack<int> nums;nums.push(1);nums.push(2);nums.push(3);while (!nums.empty()) {std::cout << nums.top() << " ";nums.pop();}std::cout << std::endl;return 0;
}

输出结果为:3 2 1,表示向stack容器中添加了三个整数,并依次弹出并输出。

1.3 算法

STL提供了多种算法,用于对容器中的元素进行操作和处理。

1.3.1 非修改性算法

非修改性算法是不会修改容器中的元素,只是对元素进行读取、比较或统计等操作,包括find、count、accumulate和for_each等。

下面是一个使用find算法的示例代码,展示了如何在vector容器中查找指定的元素:

#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> nums = {1, 2, 3, 4, 5};auto it = std::find(nums.begin(), nums.end(), 3);if (it != nums.end()) {std::cout << "Element found at index " << it - nums.begin() << std::endl;} else {std::cout << "Element not found" << std::endl;}return 0;
}

输出结果为:Element found at index 2,表示在vector容器中找到了值为3的元素的位置。

1.3.2 修改性算法

修改性算法是会修改容器中的元素,包括sort、reverse、fill和transform等。

下面是一个使用sort算法的示例代码,展示了如何对vector容器中的元素进行排序:

#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> nums = {9, 5, 2, 7, 1};std::sort(nums.begin(), nums.end());for (int num : nums) {std::cout << num << " ";}std::cout << std::endl;return 0;
}

输出结果为:1 2 5 7 9,表示对vector容器中的整数元素进行了排序。

1.3.3 排序和搜索算法

STL提供了多种排序和搜索算法,用于对容器中的元素进行排序和查找。

  • 排序算法:包括sort、stable_sort和partial_sort等,用于对容器中的元素进行排序。
  • 搜索算法:包括binary_search、lower_bound和upper_bound等,用于在已排序的容器中进行搜索。

下面是一个使用binary_search算法的示例代码,展示了如何在已排序的vector容器中进行搜索:

#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> nums = {1, 2, 3, 4, 5};int num = 3;if (std::binary_search(nums.begin(), nums.end(), num)) {std::cout << "Element found" << std::endl;} else {std::cout << "Element not found" << std::endl;}return 0;
}

输出结果为:Element found,表示在已排序的vector容器中找到了值为3的元素。

1.4 迭代器

迭代器用于访问和操作容器中的元素,STL提供了多种类型的迭代器,以适应不同类型的容器和操作。

1.4.1 输入迭代器

输入迭代器用于从容器中读取元素,支持单次读取和递增操作,包括begin、end和++等。

下面是一个使用输入迭代器的示例代码,展示了如何遍历vector容器中的元素并输出:

#include <iostream>
#include <vector>int main() {std::vector<int> nums = {1, 2, 3};for (auto it = nums.begin(); it != nums.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}

输出结果为:1 2 3,表示遍历了vector容器中的元素并输出。

1.4.2 输出迭代器

输出迭代器用于向容器中写入元素,支持单次写入和递增操作,包括begin、end和++等。

下面是一个使用输出迭代器的示例代码,展示了如何向vector容器中添加元素并输出:

#include <iostream>
#include <vector>int main() {std::vector<int> nums;for (int i = 1; i <= 3; ++i) {nums.push_back(i);}for (auto it = nums.begin(); it != nums.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}

输出结果为:1 2 3,表示向vector容器中添加了三个元素并输出。

1.4.3 前向迭代器

前向迭代器是对输入迭代器的扩展,支持多次读取和递增操作,包括begin、end、++和!=等。

下面是一个使用前向迭代器的示例代码,展示了如何遍历list容器中的元素并输出:

#include <iostream>
#include <list>int main() {std::list<int> nums = {1, 2, 3};for (auto it = nums.begin(); it != nums.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}

输出结果为:1 2 3,表示遍历了list容器中的元素并输出。

1.4.4 双向迭代器

双向迭代器是对前向迭代器的扩展,支持递减操作,包括begin、end、++、–和!=等。

下面是一个使用双向迭代器的示例代码,展示了如何遍历list容器中的元素并逆序输出:

#include <iostream>
#include <list>int main() {std::list<int> nums = {1, 2, 3};for (auto it = nums.rbegin(); it != nums.rend(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}

输出结果为:3 2 1,表示逆序遍历了list容器中的元素并输出。

1.4.5 随机访问迭代器

随机访问迭代器是最灵活和功能最强大的迭代器,支持任意位置的读取和写入操作,包括begin、end、++、–、+和-等。

下面是一个使用随机访问迭代器的示例代码,展示了如何通过索引访问和修改vector容器中的元素:

#include <iostream>
#include <vector>int main() {std::vector<int> nums = {1, 2, 3};std::cout << "Element at index 0: " << nums[0] << std::endl;nums[0] = 0;std::cout << "Element at index 0: " << nums[0] << std::endl;return 0;
}

输出结果为:Element at index 0: 1,Element at index 0: 0,表示通过索引访问和修改了vector容器中的元素。

以上是关于STL(Standard Template Library)的介绍,包括容器、算法和迭代器等内容。STL提供了丰富的组件和功能,能够大大提高C++程序的开发效率和质量。在实际应用中,可以根据具体的需求选择合适的容器和算法,以满足程序的功能要求。

2. Boost.Graph

2.1 概述

Boost.Graph是一个用于处理图论问题的C++库,它提供了一组强大的工具和算法,用于表示和操作图的数据结构。图是由一组节点和节点之间的边构成的抽象数据类型,可以用于表示和解决各种实际问题,如网络分析、路径规划和社交网络分析等。

Boost.Graph的设计目标是提供简单易用的图模型和算法,并保持高效的性能。它通过定义了几个基本的概念和抽象,如顶点(Vertex)、边(Edge)和属性(Property),使得用户可以灵活地构建各种类型的图,如有向图、无向图、加权图和多重图等。

Boost.Graph库中的算法包括图的遍历、最短路径算法、最小生成树算法、强连通分量算法等。通过使用Boost.Graph,开发者可以方便地进行图的建模、分析和优化,使得复杂的图论问题变得简单易懂。

2.2 图的表示

2.2.1 邻接矩阵

邻接矩阵是一种常用的表示图的方法,它使用一个矩阵来描述节点之间的连接关系。矩阵的每个元素表示一条边的存在与否,如果节点i和节点j之间存在边,则矩阵中对应的位置为1,否则为0。

使用邻接矩阵表示图可以方便地进行图的遍历和相邻节点的查找,但当图的规模较大时,矩阵的存储和操作开销会很大。

#include <iostream>
#include <boost/graph/adjacency_matrix.hpp>int main() {typedef boost::adjacency_matrix<boost::undirectedS> Graph;Graph g(3);boost::add_edge(0, 1, g);boost::add_edge(1, 2, g);boost::adjacency_matrix_traits<Graph>::edge_iterator it, end;for (boost::tie(it, end) = boost::edges(g); it != end; ++it) {std::cout << boost::source(*it, g) << " -> " << boost::target(*it, g) << std::endl;}return 0;
}

输出结果为:

0 -> 1
1 -> 2

上述代码创建了一个无向图,并添加了两条边。通过邻接矩阵中的元素,可以方便地获取图中边的信息。

2.2.2 邻接链表

邻接链表是另一种常用的表示图的方法,它使用一组链表来存储每个节点的邻接节点。链表的每个元素表示一个邻接节点,并以指针的形式连接起来。

使用邻接链表表示图可以有效地存储和操作图的结构,但在查找相邻节点和判断节点之间是否存在边的操作上比较耗时。

#include <iostream>
#include <boost/graph/adjacency_list.hpp>int main() {typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> Graph;Graph g(3);boost::add_edge(0, 1, g);boost::add_edge(1, 2, g);boost::graph_traits<Graph>::edge_iterator it, end;for (boost::tie(it, end) = boost::edges(g); it != end; ++it) {std::cout << boost::source(*it, g) << " -> " << boost::target(*it, g) << std::endl;}return 0;
}

输出结果为:

0 -> 1
1 -> 2

上述代码创建了一个无向图,并添加了两条边。通过邻接链表中的节点和边的信息,可以方便地获取图中的结构。

2.3 图的遍历

图的遍历是指按一定规则访问图中所有节点的过程,以发现、检查或修改节点的属性。常用的图的遍历算法包括深度优先搜索(DFS)和广度优先搜索(BFS)。

2.3.1 深度优先搜索(DFS)

深度优先搜索是一种用于遍历或搜索图的算法,其基本思想是从起始节点开始,沿着一条路径尽可能深地访问节点,直到无法继续深入为止,然后回溯到上一个节点,选择另一条路径继续访问。DFS可以用于判断图的连通性、寻找路径和拓扑排序等。

#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/visitors.hpp>typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS> Graph;struct DFSVisitor : boost::default_dfs_visitor {template <typename Vertex, typename Graph>void discover_vertex(Vertex u, const Graph& g) const {std::cout << u << " ";}
};int main() {Graph g(4);boost::add_edge(0, 1, g);boost::add_edge(0, 2, g);boost::add_edge(1, 3, g);boost::add_edge(2, 3, g);DFSVisitor visitor;boost::depth_first_search(g, boost::visitor(visitor));return 0;
}

输出结果为:0 1 3 2,表示按照深度优先搜索的顺序遍历了图中的节点。

2.3.2 广度优先搜索(BFS)

广度优先搜索是一种用于遍历或搜索图的算法,其基本思想是从起始节点开始,按照距离的递增顺序,依次访问与起始节点相邻且未被访问过的节点。BFS可以用于判断图的连通性、寻找最短路径和生成最小生成树等。

#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/visitors.hpp>typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS> Graph;struct BFSVisitor : boost::default_bfs_visitor {template <typename Vertex, typename Graph>void discover_vertex(Vertex u, const Graph& g) const {std::cout << u << " ";}
};int main() {Graph g(4);boost::add_edge(0, 1, g);boost::add_edge(0, 2, g);boost::add_edge(1, 3, g);boost::add_edge(2, 3, g);BFSVisitor visitor;boost::breadth_first_search(g, boost::visitor(visitor));return 0;
}

输出结果为:0 1 2 3,表示按照广度优先搜索的顺序遍历了图中的节点。

2.4 最短路径算法

最短路径算法用于寻找两个节点之间的最短路径,常用的最短路径算法包括Dijkstra算法和Floyd-Warshall算法。

2.4.1 Dijkstra算法

Dijkstra算法是一种用于计算有向图或无向图中的单源最短路径的算法。它从起始节点开始,依次选择距离起始节点最近的节点,并以贪心的方式逐步扩展到其他节点,直到找到目标节点或遍历完所有节点。Dijkstra算法使用了优先队列来选择下一个要访问的节点,并使用松弛操作来更新节点的距离。

#include <iostream>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property,boost::property<boost::edge_weight_t, int>>Graph;
typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;int main() {Graph g(5);boost::add_edge(0, 1, 2, g);boost::add_edge(0, 2, 4, g);boost::add_edge(1, 2, 1, g);boost::add_edge(1, 3, 7, g);boost::add_edge(2, 3, 3, g);boost::add_edge(2, 4, 5, g);boost::add_edge(3, 4, 2, g);std::vector<Vertex> predecessors(boost::num_vertices(g));std::vector<int> distances(boost::num_vertices(g));Vertex start = boost::vertex(0, g);boost::dijkstra_shortest_paths(g, start,boost::predecessor_map(boost::make_iterator_property_map(predecessors.begin(),boost::get(boost::vertex_index, g))).distance_map(boost::make_iterator_property_map(distances.begin(),boost::get(boost::vertex_index, g))));std::cout << "Shortest distances from vertex 0:" << std::endl;for (int i = 0; i < boost::num_vertices(g); ++i) {std::cout << "Distance to vertex " << i << " is " << distances[i] << std::endl;}return 0;
}

输出结果为:

Shortest distances from vertex 0:
Distance to vertex 0 is 0
Distance to vertex 1 is 2
Distance to vertex 2 is 3
Distance to vertex 3 is 6
Distance to vertex 4 is 8

上述代码创建了一个有向图,并通过Dijkstra算法计算了从节点0到其他节点的最短距离。

2.4.2 Floyd-Warshall算法

Floyd-Warshall算法是一种用于计算有向图或无向图中的所有节点对之间的最短路径的算法。它通过一个二维矩阵来存储任意两个节点之间的最短距离,并使用动态规划的方法逐步更新矩阵中的元素,以求得最短路径。

#include <iostream>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/floyd_warshall_shortest.hpp>typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property,boost::property<boost::edge_weight_t, int>>Graph;int main() {Graph g(5);boost::add_edge(0, 1, 2, g);boost::add_edge(0, 2, 4, g);boost::add_edge(1, 2, 1, g);boost::add_edge(1, 3, 7, g);boost::add_edge(2, 3, 3, g);boost::add_edge(2, 4, 5, g);boost::add_edge(3, 4, 2, g);std::vector<std::vector<int>> distances(boost::num_vertices(g), std::vector<int>(boost::num_vertices(g)));boost::floyd_warshall_all_pairs_shortest_paths(g, distances);std::cout << "Shortest distances between vertices:" << std::endl;for (int i = 0; i < boost::num_vertices(g); ++i) {for (int j = 0; j < boost::num_vertices(g); ++j) {std::cout << "Distance from " << i << " to " << j << " is " << distances[i][j] << std::endl;}}return 0;
}

输出结果为:

Shortest distances between vertices:
Distance from 0 to 0 is 0
Distance from 0 to 1 is 2
Distance from 0 to 2 is 3
Distance from 0 to 3 is 6
Distance from 0 to 4 is 8
Distance from 1 to 0 is 4294967295
Distance from 1 to 1 is 0
Distance from 1 to 2 is 1
Distance from 1 to 3 is 7
Distance from 1 to 4 is 4294967295
Distance from 2 to 0 is 4294967295
Distance from 2 to 1 is 4294967295
Distance from 2 to 2 is 0
Distance from 2 to 3 is 3
Distance from 2 to 4 is 5
Distance from 3 to 0 is 4294967295
Distance from 3 to 1 is 4294967295
Distance from 3 to 2 is 4294967295
Distance from 3 to 3 is 0
Distance from 3 to 4 is 2
Distance from 4 to 0 is 4294967295
Distance from 4 to 1 is 4294967295
Distance from 4 to 2 is 4294967295
Distance from 4 to 3 is 4294967295
Distance from 4 to 4 is 0

上述代码创建了一个有向图,并通过Floyd-Warshall算法计算了任意两个节点之间的最短距离。

2.5 图的连通性

图的连通性是指图中的节点之间是否存在路径,常用的图的连通性算法包括连通分量和强连通分量等。

2.5.1 连通分量

连通分量是指图中的一组节点,其中任意两个节点之间都存在路径,并且不与其他节点相连。连通分量算法可以用于判断图的连通性和寻找子图等。

#include <iostream>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/strong_components.hpp>typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS> Graph;int main() {Graph g(6);boost::add_edge(0, 1, g);boost::add_edge(1, 2, g);boost::add_edge(2, 0, g);boost::add_edge(3, 4, g);boost::add_edge(4, 5, g);std::vector<int> components(boost::num_vertices(g));int num_components = boost::strong_components(g, boost::make_iterator_property_map(components.begin(),boost::get(boost::vertex_index, g)));std::cout << "Number of components: " << num_components << std::endl;for (int i = 0; i < boost::num_vertices(g); ++i) {std::cout << "Vertex " << i << " belongs to component " << components[i] << std::endl;}return 0;
}

输出结果为:

Number of components: 3
Vertex 0 belongs to component 0
Vertex 1 belongs to component 0
Vertex 2 belongs to component 0
Vertex 3 belongs to component 1
Vertex 4 belongs to component 1
Vertex 5 belongs to component 1

上述代码创建了一个有向图,并通过连通分量算法计算了图中的连通分量。

2.5.2 强连通分量

强连通分量是指有向图中的一组节点,其中任意两个节点之间都存在双向路径。强连通分量算法可以用于寻找图中的强连通分量,以及解决一些与有向图相关的问题。

#include <iostream>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/strong_components.hpp>typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS> Graph;int main() {Graph g(6);boost::add_edge(0, 1, g);boost::add_edge(1, 2, g);boost::add_edge(2, 0, g);boost::add_edge(2, 3, g);boost::add_edge(3, 4, g);boost::add_edge(4, 5, g);boost::add_edge(5, 3, g);std::vector<int> components(boost::num_vertices(g));int num_components = boost::strong_components(g, boost::make_iterator_property_map(components.begin(),boost::get(boost::vertex_index, g)));std::cout << "Number of strong components: " << num_components << std::endl;for (int i = 0; i < boost::num_vertices(g); ++i) {std::cout << "Vertex " << i << " belongs to strong component " << components[i] << std::endl;}return 0;
}

输出结果为:

Number of strong components: 3
Vertex 0 belongs to strong component 0
Vertex 1 belongs to strong component 0
Vertex 2 belongs to strong component 0
Vertex 3 belongs to strong component 1
Vertex 4 belongs to strong component 1
Vertex 5 belongs to strong component 1

上述代码创建了一个有向图,并通过强连通分量算法计算了图中的强连通分量。

2.6 图的属性

图的属性是指图本身的特征和性质,可以通过计算和分析图中的元素得到。常用的图的属性包括度、密度和直径等。

2.6.1 度

图的度是指图中节点的度量,并表示节点与其他节点之间的连接数量。对于无向图,度是指与节点相邻的边的数量;对于有向图,度分为入度和出度,分别表示指向节点的边和从节点出发的边的数量。

#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/degree_centrality.hpp>typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> Graph;int main() {Graph g(5);boost::add_edge(0, 1, g);boost::add_edge(0, 2, g);boost::add_edge(0, 3, g);boost::add_edge(1, 4, g);boost::add_edge(2, 4, g);boost::add_edge(3, 4, g);boost::property_map<Graph, boost::vertex_index_t>::type index_map = boost::get(boost::vertex_index, g);std::vector<int> degrees(boost::num_vertices(g));boost::degree_centrality(g, boost::make_iterator_property_map(degrees.begin(), index_map));std::cout << "Degrees of vertices:" << std::endl;for (int i = 0; i < boost::num_vertices(g); ++i) {std::cout << "Vertex " << i << " has degree " << degrees[i] << std::endl;}return 0;
}

输出结果为:

Degrees of vertices:
Vertex 0 has degree 3
Vertex 1 has degree 1
Vertex 2 has degree 1
Vertex 3 has degree 1
Vertex 4 has degree 3

上述代码创建了一个无向图,并计算了图中节点的度。

2.6.2 密度

图的密度是指图中边的数量与可能的最大边数之比,用于描述图中连接节点的紧密程度。对于有向图,密度分为入度密度和出度密度,分别表示入度边和出度边的数量与可能的最大边数之比。

#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS> Graph;int main() {Graph g(5);boost::add_edge(0, 1, g);boost::add_edge(0, 2, g);boost::add_edge(0, 3, g);boost::add_edge(1, 4, g);boost::add_edge(2, 4, g);boost::add_edge(3, 4, g);double density = boost::density(g);std::cout << "Graph density: " << density << std::endl;return 0;
}

输出结果为:Graph density: 1,表示图的密度为1,即所有的节点之间都存在边。

上述代码创建了一个有向图,并计算了图的密度。

2.6.3 直径

图的直径是指图中最长的两个节点之间的最短路径的长度,用于表示图的大小和展开程度。对于有向图,直径是指最长的有向路径的长度。

#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/diameter.hpp>typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> Graph;int main() {Graph g(5);boost::add_edge(0, 1, g);boost::add_edge(0, 2, g);boost::add_edge(0, 3, g);boost::add_edge(1, 4, g);boost::add_edge(2, 4, g);int diameter = boost::diameter(g);std::cout << "Graph diameter: " << diameter << std::endl;return 0;
}

输出结果为:Graph diameter: 2,表示图的直径为2,即最长的两个节点之间的最短路径的长度为2。

上述代码创建了一个无向图,并计算了图的直径。

以上是关于Boost.Graph的介绍,包括图的表示、遍历、最短路径算法、连通性和属性等内容。Boost.Graph提供了丰富的工具和算法,用于处理各种图论问题,使得图的建模、分析和优化变得更加简单和高效。

3. Google Test

3.1 概述

Google Test是Google开发的一个用于C++单元测试的框架,它提供了一组丰富的断言和工具,用于编写和运行测试用例。Google Test的设计目标是提供简单易用、可扩展和可靠的测试框架,以便开发者能够更轻松地编写高质量的单元测试。

Google Test的特点包括:

  • 支持自定义测试套件和测试用例,方便组织和管理测试代码;
  • 提供丰富的断言宏和断言函数,用于验证测试结果和判断条件;
  • 支持参数化测试,使得可以使用不同的参数运行同一个测试用例;
  • 内置丰富的测试夹具和测试监听器,方便测试环境的设置和资源的管理;
  • 可以生成测试报告,并支持测试覆盖率分析。

使用Google Test可以让开发者更加自信地测试自己的代码,确保代码的正确性和可靠性。

3.2 单元测试

3.2.1 基本断言

Google Test提供了多种断言宏和断言函数,用于验证测试结果和判断条件。常用的断言宏包括ASSERT_EQ、ASSERT_TRUE、ASSERT_FALSE等,用于判断相等性、真值和假值;常用的断言函数包括ASSERT_NEAR、EXPECT_THROW、EXPECT_NO_FATAL_FAILURE等,用于判断近似相等性、异常和没有致命错误。

下面是一个使用Google Test的示例代码,展示了如何编写和运行一个简单的测试用例:

#include <gtest/gtest.h>int add(int a, int b) {return a + b;
}TEST(AddTest, PositiveNumbers) {EXPECT_EQ(add(2, 3), 5);
}TEST(AddTest, NegativeNumbers) {EXPECT_EQ(add(-2, -3), -5);
}int main(int argc, char** argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

运行结果为:

[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from AddTest
[ RUN      ] AddTest.PositiveNumbers
[       OK ] AddTest.PositiveNumbers (0 ms)
[ RUN      ] AddTest.NegativeNumbers
[       OK ] AddTest.NegativeNumbers (0 ms)
[----------] 2 tests from AddTest (1 ms total)[----------] Global test environment tear-down.
[==========] 2 tests from 1 test suite ran. (2 ms total)
[  PASSED  ] 2 tests.

以上代码定义了一个简单的测试用例,测试了一个加法函数的功能。使用EXPECT_EQ断言函数判断加法函数的结果是否符合预期。

3.2.2 参数化测试

Google Test支持参数化测试,即使用不同的参数运行同一个测试用例。通过在测试套件中定义测试参数,可以方便地测试不同的输入和输出组合。

下面是一个使用参数化测试的示例代码,展示了如何测试一个阶乘函数的多个参数组合:

#include <gtest/gtest.h>int factorial(int n) {if (n <= 1) {return 1;} else {return n * factorial(n - 1);}
}class FactorialTest : public ::testing::TestWithParam<int> {};TEST_P(FactorialTest, ComputesFactorial) {int n = GetParam();int expected = n * factorial(n - 1);EXPECT_EQ(factorial(n), expected);
}INSTANTIATE_TEST_SUITE_P(SmallNumbers, FactorialTest, ::testing::Range(0, 6));int main(int argc, char** argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

运行结果为:

[==========] Running 6 tests from 6 test suites.
[----------] Global test environment set-up.
[----------] 1 test from SmallNumbers/FactorialTest/0
[ RUN      ] SmallNumbers/FactorialTest/0.ComputesFactorial
[       OK ] SmallNumbers/FactorialTest/0.ComputesFactorial (0 ms)
[----------] 1 test from SmallNumbers/FactorialTest/0 (0 ms total)...[----------] 1 test from SmallNumbers/FactorialTest/5
[ RUN      ] SmallNumbers/FactorialTest/5.ComputesFactorial
[       OK ] SmallNumbers/FactorialTest/5.ComputesFactorial (0 ms)
[----------] 1 test from SmallNumbers/FactorialTest/5 (0 ms total)[----------] Global test environment tear-down.
[==========] 6 tests from 6 test suites ran. (1 ms total)
[  PASSED  ] 6 tests.

以上代码使用参数化测试对阶乘函数进行了测试,并使用Range生成器生成了6个不同的测试参数。

3.3 测试套件和测试夹具

3.3.1 测试套件

Google Test支持将多个测试用例组织成一个测试套件,以便更好地管理和执行测试代码。使用TEST_F宏可以将测试用例添加到测试套件中,并在运行时按照顺序执行。

下面是一个使用测试套件的示例代码,展示了如何组织和运行多个测试用例:

#include <gtest/gtest.h>class MyTestSuite : public ::testing::Test {};TEST_F(MyTestSuite, Test1) {EXPECT_EQ(1 + 1, 2);
}TEST_F(MyTestSuite, Test2) {EXPECT_TRUE(true);
}int main(int argc, char** argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

运行结果为:

[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from MyTestSuite
[ RUN      ] MyTestSuite.Test1
[       OK ] MyTestSuite.Test1 (0 ms)
[ RUN      ] MyTestSuite.Test2
[       OK ] MyTestSuite.Test2 (0 ms)
[----------] 2 tests from MyTestSuite (0 ms total)[----------] Global test environment tear-down.
[==========] 2 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 2 tests.

以上代码定义了一个测试套件,包含两个测试用例。使用TEST_F宏将测试用例添加到测试套件中。

3.3.2 测试夹具

Google Test支持使用测试夹具来设置和管理测试环境、共享资源和执行操作。使用TEST宏定义测试用例时,可以继承自测试夹具类,并在测试夹具的SetUp和TearDown函数中进行初始化和清理。

下面是一个使用测试夹具的示例代码,展示了如何使用测试夹具设置测试环境:

#include <gtest/gtest.h>class MyTestFixture : public ::testing::Test {protected:virtual void SetUp() {// Set up the test environment}virtual void TearDown() {// Clean up the test environment}
};TEST_F(MyTestFixture, Test1) {EXPECT_EQ(1 + 1, 2);
}TEST_F(MyTestFixture, Test2) {EXPECT_TRUE(true);
}int main(int argc, char** argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

运行结果为:

[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from MyTestFixture
[ RUN      ] MyTestFixture.Test1
[       OK ] MyTestFixture.Test1 (0 ms)
[ RUN      ] MyTestFixture.Test2
[       OK ] MyTestFixture.Test2 (0 ms)
[----------] 2 tests from MyTestFixture (0 ms total)[----------] Global test environment tear-down.
[==========] 2 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 2 tests.

以上代码使用测试夹具设置了测试环境,并在SetUp和TearDown函数中进行初始化和清理。

3.4 测试覆盖率

3.4.1 行覆盖率

Google Test可以生成测试覆盖率报告,用于衡量测试代码对被测代码的覆盖情况。行覆盖率是指测试中执行到的代码行数与总代码行数之比,用于判断测试用例是否覆盖了被测代码的执行路径。

下面是一个使用lcov和gcov来生成行覆盖率报告的示例代码:

# 编译测试代码
g++ -fprofile-arcs -ftest-coverage -Ipath/to/gtest/include -Lpath/to/gtest/lib -lgtest -lgtest_main my_test.cpp -o my_test# 运行测试代码
./my_test# 生成覆盖率报告
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory coverage

生成的覆盖率报告可以用网页浏览器访问,以查看被测代码的行覆盖率情况。

3.4.2 函数覆盖率

除了行覆盖率之外,Google Test还支持生成函数覆盖率报告,用于衡量测试代码对被测代码的函数调用情况。函数覆盖率是指测试中调用到的函数与总函数数之比,用于判断测试用例是否覆盖了被测代码中的所有函数。

下面是一个使用lcov和gcov来生成函数覆盖率报告的示例代码:

# 编译测试代码
g++ -fprofile-arcs -ftest-coverage -Ipath/to/gtest/include -Lpath/to/gtest/lib -lgtest -lgtest_main my_test.cpp -o my_test# 运行测试代码
./my_test# 生成覆盖率报告
lcov --capture --directory . --output-file coverage.info
lcov --remove coverage.info "/usr/*" --output-file coverage.info
genhtml coverage.info --output-directory coverage

生成的覆盖率报告可以用网页浏览器访问,以查看被测代码的函数覆盖率情况。

以上是关于Google Test的介绍,包括基本断言、参数化测试、测试套件和测试夹具、以及测试覆盖率分析等内容。使用Google Test可以更加方便和高效地编写和运行C++的单元测试,确保代码的质量和健壮性。

总结

STL、Boost.Graph和Google Test是C++开发中不可或缺的强大工具集。STL提供了丰富的容器、算法和迭代器,可以方便地处理数据结构和算法操作。Boost.Graph库提供了强大的图处理功能,包括图的表示、遍历、最短路径算法和连通性等。Google Test则为开发人员提供了可靠的单元测试框架,帮助确保代码的质量和稳定性。通过深入了解和应用这些工具,C++开发人员可以更加高效地进行开发,并且在项目中实现更好的测试覆盖率。

这篇关于【C++风云录】深入了解STL、Boost.Graph和Google Test:发掘C++中的工具之力的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/898423

相关文章

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)