深入理解gtest C/C++单元测试经验谈

2023-11-16

Google C++ Testing Framework(简称gtest,http://code.google.com/p/googletest/)是Google公司发布的一个开源C/C++单元测试框架,已被应用于多个开源项目及Google内部项目中,知名的例子包括Chrome Web浏览器、LLVM编译器架构、Protocol Buffers数据交换格式及工具等。

优秀的C/C++单元测试框架并不算少,相比之下gtest仍具有明显优势。与CppUnit比,gtest需要使用的头文件和函数宏更集中,并支持测试用例的自动注册。与CxxUnit比,gtest不要求Python等外部工具的存在。与Boost.Test比,gtest更简洁容易上手,实用性也并不逊色。Wikipedia给出了各种编程语言的单元测试框架列表(http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks)。

一、基本用法

gtest当前的版本是1.5.0,如果使用Visual C++编译,要求编译器版本不低于7.1(Visual C++ 2003)。如下图所示,它的msvc文件夹包含Visual C++工程和项目文件,samples文件夹包含10个使用范例。

一般情况下,我们的单元测试代码只需要包含头文件gtest.h。gtest中常用的所有结构体、类、函数、常量等,都通过命名空间testing访问,不过gtest已经把最简单常用的单元测试功能包装成了一些带参数宏,因此在简单的测试中常常可以忽略命名空间的存在。

按照gtest的叫法,宏TEST为特定的测试用例(Test Case)定义了一个可执行的测试(Test)。它接受用户指定的测试用例名(一般取被测对象名)和测试名作为参数,并划出了一个作用域供填充测试宏语句和普通的C++代码。一系列TEST的集合就构成一个简单的测试程序。
常用的测试宏如下表所示。以ASSERT_开头和以EXPECT_开头的宏的区别是,前者在测试失败时会给出报告并立即终止测试程序,后者在报告后继续执行测试程序。

ASSERT宏
EXPECT宏
功能
ASSERT_TRUE
EXPECT_TRUE
判真
ASSERT_FALSE
EXPECT_FALSE
判假
ASSERT_EQ
EXPECT_EQ
相等
ASSERT_NE
EXPECT_NE
不等
ASSERT_GT
EXPECT_GT
大于
ASSERT_LT
EXPECT_LT
小于
ASSERT_GE
EXPECT_GE
大于或等于
ASSERT_LE
EXPECT_LE
小于或等于
ASSERT_FLOAT_EQ
EXPECT_FLOAT_EQ
单精度浮点值相等
ASSERT_DOUBLE_EQ
EXPECT_DOUBLE_EQ
双精度浮点值相等
ASSERT_NEAR
EXPECT_NEAR
浮点值接近(第3个参数为误差阈值)
ASSERT_STREQ
EXPECT_STREQ
C字符串相等
ASSERT_STRNE
EXPECT_STRNE
C字符串不等
ASSERT_STRCASEEQ
EXPECT_STRCASEEQ
C字符串相等(忽略大小写)
ASSERT_STRCASENE
EXPECT_STRCASENE
C字符串不等(忽略大小写)
ASSERT_PRED1
EXPECT_PRED1
自定义谓词函数, (pred, arg1)(还有_PRED2, ...,  _PRED5

 
写个简单的测试试一下。假设我们实现了一个加法函数:
 
 
  1. // add.h  
  2. #pragma once  
  3. inline int Add(int i, int j) { return i+j; } 
对应的单元测试程序可以这样写:
 
 
  1. // add_unittest.cpp  
  2. #include "add.h"  
  3. #include <gtest/gtest.h>  
  4.    
  5. TEST(Add, 负数) {  
  6.  EXPECT_EQ(Add(-1,-2), -3);  
  7.  EXPECT_GT(Add(-4,-5), -6); // 故意的  
  8. }  
  9.    
  10. TEST(Add, 正数) {  
  11.  EXPECT_EQ(Add(1,2), 3);  
  12.  EXPECT_GT(Add(4,5), 6);  
代码中,测试用例Add包含两个测试,正数和负数(这里利用了Visual C++ 2005以上允许标识符包含Unicode字符的特性)。编译运行效果如下:
在控制台界面中,通过的测试用绿色表示,失败的测试用红色表示。双横线分隔了不同的测试用例,其中包含的每个测试的启动与结果用单横线和RUN ... OK或RUN ... FAILED标出。失败的测试会打印出代码行和原因,测试程序最后为所有用例和测试显示统计结果。建议读者试一下换成ASSERT_宏的不同之处。
每个测试宏还可以使用<<运算符在测试失败时输出自定义信息,如:
 
 
  1. ASSERT_EQ(M[i], N[j]) << "i = " << i << ", j = " << j; 
编译命令行中,gtest_mt.lib和gtest_main_mt.lib就是前面使用VC项目文件生成的静态库。有意思的是,测试代码不需要注册测试用例,也不需要定义main函数,这是gtest通过后一个静态库自动完成的,它的实现代码如下:
 
 
  1. // gtest-main.cc  
  2. int main(int argc, char **argv) {  
  3.  std::cout << "Running main() from gtest_main.cc\n";  
  4.  testing::InitGoogleTest(&argc, argv);  
  5.  return RUN_ALL_TESTS();  
其中,函数InitGoogleTest负责注册需要运行的所有测试用例,宏RUN_ALL_TEST负责执行所有测试,如果全部成功则返回0,否则返回1。当然,我们也可以仅链接gtest_mt.lib,自己提供main函数。
二、测试固件
很多时候,我们想在不同的测试执行前创建相同的配置环境,在测试执行结束后执行相应的清理工作,测试固件(Test Fixture)为这种需求提供了方便。“Fixture”是一个汉语中不易直接对应的词,《美国传统词典》对它的解释是“(作为附属物的)固定装置;被固定的状态”。在单元测试中,Fixture的作用是为测试创建辅助性的上下文环境,实现测试的初始化和终结与测试过程本身的分离,便于不同测试使用相同代码来搭建固定的配置环境。用体操比赛的说法,测试过程体现了特定测试的自选动作,测试固件则体现了对一系列测试(在开始和结束时)的规定动作。有些讲单元测试的书籍直接把测试固件称为Scaffolding(脚手架)。
使用测试固件比单纯调用TEST宏稍微麻烦一些:
1.          从gtest的testing::Test类派生一个类,用public或protected定义以下所有成员。
2.          (可选)建立环境:使用默认构造函数,或定义一个虚成员函数virtual void SetUp()。
3.          (可选)销毁环境:使用析构函数,或定义一个虚成员函数virtual void TearDown()。
4.          用TEST_F定义测试,写法与TEST相同,但测试用例名必须为上面定义的类名。
每个带固件的测试的执行顺序是:
1.          调用默认构造函数创建一个新的带固件对象。
2.          立即调用SetUp函数。
3.          运行TEST_F体。
4.          立即调用TearDown函数。
5.          调用析构函数销毁类对象。

从gtest的实现代码可以看到,TEST_F又从用户定义的类自动派生了一个类,因此要求public或protected的访问权限;大括号里的内容被扩展成一个名为TestBody的虚成员函数的函数体,因此可以在其中直接访问成员变量和成员函数。其实TEST也采用了相同的实现机制,只是它直接从gtest的testing::Test自动派生类,所以可以指定任意用例名。testing::Test类的SetUp和TearDown都是空函数,所以它只执行测试步骤,没有环境的创建和销毁。
借用上面Add函数写个固件测试的例子:
 
 
  1. // add_unittest2.cpp  
  2. #include "add.h"  
  3. #include <stdio.h>  
  4. #include <gtest/gtest.h>  
  5.    
  6. class AddTest: public testing::Test  
  7. {  
  8. public:  
  9.  virtual void SetUp()    { puts("SetUp()"); }  
  10.  virtual void TearDown() { puts("TearDown()"); }  
  11. };  
  12.    
  13. TEST_F(AddTest, 正数) {  
  14.  ASSERT_GT(Add(1,2), 3); // 故意的  
  15.  ASSERT_EQ(Add(4,5), 6); // 也是故意的  
编译运行效果如下:
必须强调,每个TEST_F开始都创建了一个新的带固件对象,因此每个测试都使用独立的完全相同的初始环境,各测试可以按任意顺序执行(参见--gtest_shuffle命令行选项)。但在某些情况下,我们可能需要在各个测试间共享一个相同的环境来保存和传递状态,或者环境的状态是只读的,可以只初始化一次,再或者创建环境的过程开销很高,要求只初始化一次。共享某个固件环境的所有测试合称为一个“测试套件”(Test Suite),gtest中利用静态成员变量和静态成员函数实现这个概念:
1.          (可选)在testing::Test的派生类中,定义若干静态成员变量来维护套件的状态。
2.          (可选)建立共享环境:定义一个静态成员函数static void SetUpTestCase()。
3.          (可选)销毁共享环境:定义一个静态成员函数static void TearDownCase()。
另外,还可以使用gtest的Environment类来建立和销毁所有测试共用的全局环境(对应于上图显示的“Global test environment set-up”和“Global test environment tear-down”):
 
 
  1. class Environment {  
  2.  public:  
  3.  virtual ~Environment() {}  
  4.  virtual void SetUp() {}  
  5.  virtual void TearDown() {}  
  6. }; 
gtest文档建议测试程序自己定义main函数并在其中创建和注册全局环境对象:
 
 
  1. Environment* AddGlobalTestEnvironment(Environment* env); 
三、异常测试
C程序中要返回出错信息,可以利用特定的函数返回值、函数的输出(outbound)参数、或者设置全局变量(如C标准库定义的errno,Windows API中的“上次错误”(last error)代码,Winsock中与每个socket相关联的错误代码)。C++程序常用异常(exception)来返回出错信息,gtest为异常测试提供了专用的测试宏:

ASSERT宏
EXPECT宏
功能
ASSERT_NO_THROW
EXPECT_NO_THROW
不抛出异常,参数为 (statement)
ASSERT_ANY_THROW
EXPECT_ANY_THROW
抛出异常,参数为 (statement)
ASSERT_THROW
EXPECT_THROW
抛出特定类型的异常,参数为 (statement, type)

 
需要注意,这些测试宏都接受C/C++语句作为参数,所以既可以像前面那样传递表达式,也可以传递用大括号包起来的代码块。
借助下面的被测函数:
 
 
  1. // divide.h  
  2. #pragma once  
  3. #include <stdexcept>  
  4.    
  5. int divide(int dividend, int divisor) {  
  6.  if(!divisor) {  
  7.     throw std::length_error("can't be divided by 0"); // 故意的  
  8.  }  
  9.  return dividend / divisor;  
测试程序如下:
 
 
  1. // divide-unittest.cpp  
  2. #include <gtest/gtest.h>  
  3. #include "./divide.h"  
  4.    
  5. TEST(Divide, ByZero) {  
  6.  EXPECT_NO_THROW(divide(-1, 2));  
  7.    
  8.  EXPECT_ANY_THROW({  
  9.     int k = 0;  
  10.     divide(k, k);  
  11.  });  
  12.    
  13.  EXPECT_THROW(divide(100000, 0), std::invalid_argument);  
编译运行效果如下

容易想到,gtest的这些异常测试宏是用C++的try ... catch语句来实现的:
 
 
  1. try {  
  2.  statement;  
  3. }  
  4. catch(type const&) {  
  5.  // throw  
  6. }  
  7. catch(...) {  
  8.  // any throw  
  9. }  
  10. // no throw 
如果把上图中Visual C++的编译选项/EHsc换成/EHa,try ... catch就可以同时支持C++风格的异常和Windows系统的结构化异常(SEH)。这样,即使删掉divide函数里的if判断,测试代码的EXPECT_ANY_THROW宏也会成功捕获异常。
遗憾的是,目前仅使用这些测试宏无法得到获得被抛出异常的详细信息(如divide函数中的报错文本),这和gtest自身不愿意使用C++异常有关。
四、值参数化测试
有些时候,我们需要对代码实现的功能使用不同的参数进行测试,比如使用大量随机值来检验算法实现的正确性,或者比较同一个接口的不同实现之间的差别。gtest把“集中输入测试参数”的需求抽象出来提供支持,称为值参数化测试(Value Parameterized Test)。
值参数化测试包括4个步骤:
1.          从gtest的TestWithParam模板类派生一个类(记为C),模板参数为需要输入的测试参数的类型。由于TestWithParam本身是从Test派生的,所以C就成了一个测试固件类。
2.          在C中,可以实现诸如SetUp、TearDown等方法。特别地,测试参数由TestWithParam实现的GetParam()方法依次返回。
3.          使用TEST_P(而不是TEST_F)定义测试。
4.          使用INSTANTIATE_TEST_CASE_P宏集中输入测试参数,它接受3个参数:任意的文本前缀,测试类名(这里即为C),以及测试参数值序列。gtest框架依次使用这些参数值生成测试固件类实例,并执行用户定义的测试。
gtest提供了专门的模板函数来生成参数值序列,如下表所示:

参数值序列生成函数
含义
Bool()
生成序列 {false, true}
Range(begin, end[, step])
生成序列 {begin, begin+step, begin+2*step,  ...} (不含 end), step默认为1
Values(v1, v2,  ...,  vN)
生成序列 {v1, v2,  ...,  vN}
ValuesIn(container)ValuesIn(iter1, iter2)
枚举STL  container,或枚举迭代器范围 [iter1, iter2)
Combine(g1, g2,  ...,  gN)
生成 g1g2, ...,  gN的笛卡尔积,其中g1 g2, ...,  gN均为参数值序列生成函数(要求C++0x的<tr1/tuple>

 
写个小程序试一下。假设我们实现了一种快速累加算法,希望使用另一种直观算法进行正确性校验。算法实现和测试代码如下
 
 
  1. // addupto.h  
  2.    
  3. #pragma once  
  4.    
  5. inline unsigned NaiveAddUpTo(unsigned n) {  
  6.     unsigned sum = 0;  
  7.     for(unsigned i = 1; i <= n; ++i) sum += i;  
  8.     return sum;  
  9. }  
  10.    
  11. inline unsigned FastAddUpTo(unsigned n) {  
  12.     return n*(n+1)/2;  
测试程序如下:
 
 
  1. // addupto_test.cpp  
  2.    
  3. #include <gtest/gtest.h>  
  4. #include "addupto.h"  
  5.    
  6. class AddUpToTest : public testing::TestWithParam<unsigned>  
  7. {  
  8. public:  
  9.     AddUpToTest() { n_ = GetParam(); }  
  10. protected:  
  11.     unsigned n_;  
  12. };  
  13.    
  14. TEST_P(AddUpToTest, Calibration) {  
  15.     EXPECT_EQ(NaiveAddUpTo(n_), FastAddUpTo(n_));  
  16. }  
  17.    
  18. INSTANTIATE_TEST_CASE_P(  
  19.     NaiveAndFast, // prefix  
  20.     AddUpToTest,   // test case name  
  21.     testing::Range(1u, 1000u) // parameters  
  22. ); 
 
注意TestWithParam的模板参数设置为unsigned类型,而在代码倒数第2行,两个常量值都加了u后缀来指定为unsigned类型。熟悉C++的读者应该知道,模板函数在进行类型推断(deduction)时匹配相当严格,不像普通函数那样允许类型提升(promotion)。如果上面省略u后缀,就会造成编译错误。当然还可以显式指定模板参数:testing::Range<unsigned>(1, 1000)。
运行效果如下,这里省略了开头的大部分输出(命令行窗口设置的缓冲区高度为3000行)。

作者简介
杨玚,1980年生,2009年毕业于中国科学技术大学,获博士学位。2009年8月加入中国软件评测中心重大专项测试部,任开发测试工程师,负责“软件测试能力优化升级” 项目工具研发。关注领域为网络信息安全。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入理解gtest C/C++单元测试经验谈 的相关文章

  • Rspec 控制器测试,传递 JSON 参数

    我试图实现以下目标 在 RSpec 控制器测试中创建 POST json 请求 并向其传递参数 这是我的代码 it returns access token do post login email bla password bla1 for
  • C# .NET 4.0 测试框架?

    如果我没记错的话 NUnit 是单元测试事实上的标准 但我刚刚下载了它 编写了一个简单的测试 然后显然我必须启动 GUI 并加载我的 exe组装 根本就失败了 我尝试编辑 C Program Files x86 NUnit 2 5 7 bi
  • 开玩笑 setTimeout 不暂停测试

    it has working hooks async gt setTimeout gt console log Why don t I run expect true toBe true 15000 我已经查看了这个答案 Jest 文档和几
  • 与 White 的集成测试[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 有人有白色框架的经验吗 www co
  • 如何限制Cargo.toml中的测试线程数?

    我的测试共享公共资源并且无法同时执行 这些测试失败cargo test 但与RUST TEST THREADS 1 cargo test 我可以修改测试以等待全局互斥体 但如果有任何更简单的方法来强制 我不想让它们混乱cargo为我设置这个
  • CakePHP 控制器测试:模拟 Auth 组件

    情况 控制器代码
  • 运行连接到 Django 测试数据库的 Celery Worker

    背景 我正在开发一个项目 该项目使用 Celery 来安排将在未来某个时间运行的任务 这些任务推动最终状态机的状态向前发展 这是一个例子 未来的提醒计划在 2 天内发送给用户 当该计划任务运行时 会发送一封电子邮件 并且 FSM 会前进到下
  • 在 Slim Framework 3 中访问课堂上的应用程序

    当路由位于与 index php 不同的类中时 我无法理解如何访问 Slim 的实例 当使用 Slim Framework 2 时 我总是使用以下内容 但它在 Slim 3 中不起作用 this gt app Slim Slim getIn
  • 在 Cypress 中提取部分文本

    我是 Cypress 的新手 我需要从我的应用程序中提取文本的动态部分 div Hello World greeting number 9123 div 在此示例中 我需要从 div 中提取 9123 以便稍后在测试中使用 知道我应该怎么做
  • 将 gcov 与 CMake/CDash 结合使用的详细指南?

    我在我的项目中使用 CMake 并设置了 cdash 服务器以进行连续 夜间构建 一切运行良好 通过设置 crontab 我们可以将每小时 每晚的构建 测试结果自动上传到我们的 cdash 服务器 我的下一步是将测试覆盖率报告添加到构建中
  • Selenium 查看鼠标/指针

    有什么方法可以在运行测试时真正看到硒鼠标吗 要么是 Windows 光标图像 要么是某种点或十字线或任何东西 我正在尝试使用拖放功能selenium and java in an HTML5Web 应用程序 并且能够看到光标以了解它实际在做
  • 有没有 API 可以在两个 iphone/ipod Touch/ipad 之间共享数据? (GameKit 除外)[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有没有任何框架 API可以轻松找到其他设备并在它们之间共享数据 请不是游戏包 bonjour 会很棒
  • 在 laravel 5 测试期间如何模拟外部 API?

    我想在 laravel 中测试 HTTP 路由 URL 的操作函数调用辅助函数 该函数调用外部 API 测试时如何模拟外部 API 调用 public function actionFunction helper new HelperCla
  • 使用 RSpec 测试控制器时模拟 CanCan 授权

    这是我要测试的控制器 class UsersController lt ApplicationController load and authorize resource def index users User all respond t
  • 如何在 Kotlin 中模拟和验证 Lambda 表达式?

    在 Kotlin 和 Java 8 中 我们可以使用 Lambda 表达式来删除样板回调接口 例如 data class Profile val name String interface ProfileCallback fun onSuc
  • 在 Xcode 3.2 中使用第三方框架时出现问题

    我正在尝试使用第三方库 MPOAuthConnection 设置自己的项目来连接到 oauth REST API 到目前为止我已经完成的步骤 下载 MPOAuthConnection 的源代码 构建源代码并在 产品 选项卡中生成 MPOAu
  • 如何从 Windows 10 卸载 .net Framework 4.8

    我安装了 Windows 10 和 net Framework 4 7 2 并且我的程序运行良好 然后我想尝试一下 net Framework 4 8 安装后 我的程序无法运行 我试图卸载 net Framework 4 8 我从控制面板进
  • 找出在 html5 Canvas 上单击了哪个对象

    假设我有一个 html5 canvas 应用程序 可以在其中将对象放置在绘图画布上 某种图表编辑器 例如 Visio 但更简单 是否有一个框架可以帮助我找到单击 拖动的对象 一个选项是捕获单击事件并迭代我的所有对象 以半智能方式 并检查它是
  • 框架时代的封装

    在我以前的 C 工作中 我们总是非常小心地封装成员变量 并且仅在绝对必要时才将它们作为属性公开 我们有非常具体的构造函数来确保您在使用对象之前完全构造了该对象 如今 使用 ORM 框架 依赖注入 序列化等 似乎您最好只依赖默认构造函数并在属
  • 了解 ASP.NET 应用程序文件夹

    ASP NET 中的应用程序文件夹用于存储对运行网站至关重要的各种元素 我想更深入地了解这些文件夹 特别是文件夹的可访问性 根据有关的文章ASP NET 网站布局 http msdn microsoft com en us library

随机推荐

  • uni-app 1、app-plus的使用,#ifdef MP只兼容小程序

    最近开始查看uni app的一些项目 在pages json里面发现app plus 百度了下看见一些网友的解释是app跟h5端执行 小程序则不执行 只测试过微信小程序 据说其他小程序也不执行 代码如下 path pages index i
  • mysql中的全文索引

    查询操作在数据量比较少时 可以使用like模糊查询 但是对于大量的文本数据检索 效率很低 如果 使用全文索引 查询速度会比like快很多倍 在MySQL 5 6 以前的版本 只有MyISAM存储引擎支持全 文索引 从MySQL 5 6开始M
  • 《云计算-刘鹏》学习笔记-第一章:大数据与云计算

    文章目录 0 笔记说明 1 大数据时代 2 云计算 大数据的计算 3 云计算发展现状 4 云计算实现机制 5 云计算压倒性的成本优势 0 笔记说明 参考书籍为 云计算 第三版 作者为刘鹏 1 大数据时代 大数据的定义如下 海量数据或巨量数据
  • 概率论基础(sigma域、测度)

    一 样本空间 事件 样本空间 Omega 指一个实验的可能结果的集合 omega in Omega 称为 样本
  • 定时任务及分布式定时任务注意事项

    一 定时任务默认是阻塞的 定时任务默认是阻塞的 即串行执行 若一个服务配置多个定时任务 需要等上一个定时任务执行完 才能执行下一个定时任务 一个定时任务超长了 也不应该阻塞其他定时任务的执行 如一个定时任务每秒执行 而业务执行时间是5秒 那
  • vscode (1)直接编译

    第一步创建c文件 第二步配置 终端 配置默认生成任务 选择 usr bin gcc version 2 0 0 tasks type cppbuild label C C gcc 生成活动文件 command usr bin gcc 使用的
  • Stacked Queries(堆叠注入)

    文章目录 基本知识 原理介绍 堆叠注入的局限性 Mysql数据库实例介绍 CTF 实战与各种姿势 修改表名 利用HANDLER语句 利用MySql预处理 正常利用 MySql预处理配合十六进制绕过关键字 MySql预处理配合字符串拼接绕过关
  • jax安装

    Windows 安装 pip install jaxlib 0 3 5 f https whls blob core windows net unstable index html pip install jaxlib cuda111 f
  • Android开发工作中遇到的重点和难点总结

    1 Android N floating widget无法显示 统一管理一个window token解决了此问题 2 Pop up window在Android6 0上出现花屏 3 由于状态栏的影响 悬浮窗上下跳动 4 Wi Fi安全的数据
  • Windows下的oracle 11g的入门

    图全部都挂了 写的太累了 有空再来更 几个月没用oracle之后 花了一个下午把oracle的基本操作迅速捡回来了 记录如下 一 安装oracle11G 1 1 首先要下载oracle服务端和客户端 官网下载链接如下 http www or
  • content-type的几种取值

    目录 Content Type的几种取值 1 text plain 2 text html 3 application json 4 application xml 5 image jpeg 6 image png 7 audio mpeg
  • Socket编程基础

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 基于TCP的socket通信流程 二 基于UDP的socket通信流程 三 TCP协议下socket编程主要API接口介绍 1 int socket in
  • 【Java】迭代器之:Iterable & Iterator

    在我们Java中的迭代器是一个接口 名为Iterator 他有两个抽象方法 hasNext方法 用来判断还有没有数据访问 next方法 用来访问集合的下一个数据 迭代器可以访问不同特性的集合数据 而无需关心他们的内部实现 注意 集合并不是直
  • 小学奥数题使用python解决(使用2倒9中不重复的数使得{}+{}={}{}-{}=1{}满足)

    使用2 9中不重复的数使得 1 满足 样子不太好看 1 利用for循环和if语句 代码 利用2 9不重复的数使得 1 i 0 for a1 in range 2 10 for a2 in range 2 10 if a1 a2 and a1
  • 新学期阅读计划

    1 再认真阅读 设计模式之禅 在理解的基础上应用设计模式 2 编程之美 共4章 61个有意思的题目 3 图书馆借阅 算法导论 4 再阅读 算法之道 5 了解 操作系统导论 真正理解不要死记硬背 6 反复多次阅读经典的论文 特别是及时和师姐多
  • 部署篇-Zabbix中文乱码字符集的修正

    部署zabbix监控后默认是英文 默认不支持中文字符集 切换成中文后会出现以下情况 解决方案 从Window服务器找到相应的字休复制到zabbix Server服务器上 控制面板 字体 选择一种中文字体 建议simkai ttf root
  • Java堆和栈应用实例

    以下是一个简单的Java程序 演示了Java堆和栈的应用实例 public class HeapAndStackExample public static void main String args 创建一个对象并分配在堆内存中 Perso
  • CTFshow web入门---web56

    CTFshow web入门 web56 题目 题目分析 查看本题 发现本题为命令执行类题目 但是有一个很致命的点 那么就是他过滤了所有的字母和数字 以及一系列的符号 因此本题最值得推敲的点就是如何实现无字母数字的命令执行 通过拜读P神的一篇
  • 关系型数据库RDBMS -MySQL基础入门(三)数据分片

    数据分片 相关概念 分库分表 分片 存在一台数据库的服务器中的数据 特定方式拆分 分散存放在多台数据库服务中 达到单台服务器负载的效果 垂直分割 纵向切分 按业务类型 什么是垂直分割 纵向切分 把单一的表 拆分成多个表 并分散到不同的数据库
  • 深入理解gtest C/C++单元测试经验谈

    Google C Testing Framework 简称gtest http code google com p googletest 是Google公司发布的一个开源C C 单元测试框架 已被应用于多个开源项目及Google内部项目中