构造函数边界测试需验证临界输入下的崩溃、泄漏、误初始化或静默失败;覆盖参数越界、资源分配失败、浅拷贝陷阱及初始化列表与成员声明顺序不一致等场景。

构造函数的边界测试不是测“它能不能编译通过”,而是测“它在临界输入下会不会崩溃、泄漏、误初始化或静默失败”。
构造函数参数越界时的行为验证
构造函数常接收原始值(如 int size、const char* str、std::string_view sv),这些值可能来自用户输入、配置文件或网络,必须覆盖非法范围。
- 对数值型参数:测试
0、-1、INT_MAX、SIZE_MAX等边界值,观察是否抛出std::invalid_argument或触发断言 - 对指针/字符串类参数:传入
nullptr、空字符串、超长字符串(如 1MB)、含嵌入\0的缓冲区,确认是否拒绝构造或正确截断 - 避免仅靠
EXPECT_NO_THROW:要配合EXPECT_DEATH(启用GTEST_HAS_DEATH_TEST=1)或检查资源状态(如堆内存是否泄漏)
资源分配失败路径的模拟
构造函数若内部调用 new、malloc、open() 或 fopen(),必须验证其在失败时能否安全回滚——这是最容易被忽略的边界。
- 用
testing::MockAllocator或重载全局operator new注入std::bad_alloc异常(注意:需在测试前设置std::set_new_handler) - 若构造函数打开文件,提前
ulimit -n 0或关闭所有 fd 模拟EMFILE错误 - 重点检查:异常抛出后,已分配的资源(如部分申请的内存、已创建的临时文件)是否被完全清理,不会导致析构函数二次释放
拷贝/移动构造中的浅拷贝陷阱测试
当类持有裸指针(如 char*)或句柄(如 int fd)时,编译器自动生成的拷贝构造函数会引发浅拷贝问题——这本质是构造函数语义边界的失效。
- 显式定义拷贝构造函数后,用两个对象交叉生命周期测试:
String a("hello"); String b = a;,然后a析构后再访问b成员 - 用
valgrind --tool=memcheck或 ASan 运行测试,确认无use-after-free或double-free - 对移动构造函数,验证原对象是否进入有效但未定义状态(如指针置为
nullptr),而非残留 dangling 指针
初始化列表与成员顺序依赖的隐式边界
构造函数体内的赋值和初始化列表中成员的声明顺序不一致时,会触发未定义行为——尤其当某成员初始化依赖另一个尚未构造完成的成员时。
- 检查类中成员变量声明顺序(如
A a_; B b_;)是否与初始化列表顺序(如: b_(...), a_(b_.value()))冲突 - 这种错误在 GCC/Clang 下可能静默通过,但 MSVC 会警告 C4355;用
-Wreorder编译并开启测试构建 - 真实案例:某容器类在初始化列表中先初始化
capacity_,再用capacity_初始化内部buffer_,但声明顺序反了 →buffer_用到未初始化的capacity_
边界测试最难的不是写断言,而是识别哪些“看似合法”的输入其实暴露了设计缺陷——比如一个接受 size_t n 的构造函数允许 n == 0,但它后续逻辑却假设 n > 0,这种隐式契约必须用测试固化下来。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











