必须用 template<typename T> 声明栈类,声明与定义须同在头文件;top()/pop() 需检查空栈并抛异常或断言;推荐用 std::vector<T> 作底层容器;自定义栈仅适用于需迭代器、限容、日志等特殊场景;须显式默认移动语义以防深拷贝性能问题。

用 template 定义栈类,别写 class Stack
直接写 class Stack 会报错——C++ 模板类必须显式声明泛型参数。不加 template<typename T>,编译器根本不知道你打算支持什么类型。
常见错误是把模板定义和实现拆到 .h / .cpp 里:模板的声明和定义必须在同一个头文件中(通常全放在 .h),否则链接时找不到实例化代码。
-
template<typename T>是必需前缀,typename和class在这里等价,但习惯用typename - 成员函数(如
push()、top())也要在函数体外加template<typename T>,或写成类内定义(更简单) - 避免用
std::vector<T>做底层容器?可以,但得自己管内存;用它最省事,且vector的push_back()/pop_back()天然匹配栈行为
构造函数和 top() 要检查空栈,否则运行时崩
很多初学者只关注 push/pop 逻辑,忘了 top() 和 pop() 在空栈下调用是未定义行为——可能读到野指针、触发段错误,也可能静默返回垃圾值。
这不是编译错误,是运行期陷阱。尤其在调试模式下没崩,发布版突然 crash,很难定位。
立即学习“C++免费学习笔记(深入)”;
- 在
top()开头加if (data_.empty()) throw std::runtime_error("stack is empty"); -
pop()同理,空时抛异常 or 断言(assert(!data_.empty())),别默默 return - 如果想静默失败,至少返回
std::optional<T>(C++17 起),而不是裸指针或默认构造的T{}
std::stack 已存在,自己写只为练手或定制需求
标准库的 std::stack 就是模板类,接口干净(push()、pop()、top()、empty()、size()),底层用 deque(可换为 vector 或 list)。
除非你要:支持迭代器遍历、带容量限制、记录操作日志、或和特定内存池绑定——否则真没必要重造轮子。
- 自定义栈的典型合理场景:嵌入式环境禁用异常,需用
bool try_pop(T& out)替代top()+pop() - 性能上,
std::stack和手写 vector 版几乎无差别;但手写容易漏移动语义(push(T&&))、完美转发(emplace()) - 兼容性:C++11 起所有主流编译器都支持模板类,不用操心老版本
拷贝/移动语义不写,默认行为可能出问题
模板类不会自动推导移动构造函数。如果你的栈内部用 std::vector<T>,而 T 是大对象(比如含动态数组的类),默认拷贝构造会深拷贝整个 vector,代价高且未必需要。
更危险的是:若你手动写了析构函数(比如加了日志),编译器就不会生成移动函数,导致传值返回栈对象时强制走拷贝,甚至编译失败(“use of deleted function”)。
- 显式默认移动操作:
Stack(Stack&&) = default;和Stack& operator=(Stack&&) = default; - 如果要控制行为(比如禁止拷贝),就
= delete对应函数 - 测试点:写个函数返回局部栈对象,看是否触发移动而非拷贝(加打印日志或断点)
泛型栈看着简单,但空状态处理、异常安全、移动语义、模板实例化位置——这四点漏一个,上线后都可能变成深夜告警。










