应使用size_t而非int作为栈顶索引,因其无符号特性可避免负数下溢导致的逻辑错误,empty()必须用top_idx == 0判断,且所有边界检查需适配size_t的自然回绕行为。

怎么用模板写一个数组栈,不依赖 STL
直接上手写的话,std::stack 虽好,但面试或嵌入式场景常要求手写底层。核心是封装一个固定大小的 std::array 或裸数组,加上 top_index 记录栈顶位置,所有操作都围绕这个索引展开。
关键点:模板参数要支持任意类型,构造时指定容量;push() 和 pop() 必须检查边界;top() 不应修改状态,且必须保证非空时才调用。
示例骨架:
template <typename t size_t n>
class ArrayStack {
T data[N];
size_t top_idx = 0;
<p>public:
bool empty() const { return top_idx == 0; }
size_t size() const { return top_idx; }
bool full() const { return top_idx == N; }</p>
<pre class="brush:php;toolbar:false;">void push(const T& x) {
if (full()) throw std::runtime_error("stack overflow");
data[top_idx++] = x;
}
void pop() {
if (empty()) throw std::runtime_error("stack underflow");
--top_idx;
}
const T& top() const {
if (empty()) throw std::runtime_error("stack is empty");
return data[top_idx - 1];
}
};
为什么用 size_t 而不是 int 做栈顶索引
数组下标和容量都是无符号整型,用 int 容易在判空时出问题:比如 top_idx-- 后变成 -1,但 size_t 会回绕成极大值(如 18446744073709551615),导致 empty() 判断永远为假,后续访问越界。
所以必须统一用 size_t,且所有边界检查都要基于它自然行为来写:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
empty()只能写成top_idx == 0,不能写top_idx -
top()取元素前必须先确认!empty(),否则data[top_idx - 1]就是非法内存访问 - 构造函数里不要对
top_idx做负数初始化——根本没意义
push() 失败时该抛异常还是返回布尔值
取决于使用场景。嵌入式或实时系统通常禁用异常,这时应把 push() 改成返回 bool:
bool push(const T& x) {
if (full()) return false;
data[top_idx++] = x;
return true;
}
而普通应用开发中,用异常更符合 C++ 惯例,也避免调用方漏判错误。注意:如果用了异常,记得包含 <stdexcept></stdexcept>;如果选返回值,所有接口(pop、top)也得保持风格一致——pop() 可返回 bool,top() 就只能提供 const T& top_unchecked() 这类不安全接口,或者加输出参数。
别混用两种错误处理方式,否则调用逻辑会混乱。
模板参数 N 必须是编译期常量,不能传变量
因为数组大小要确定,T data[N] 是栈对象的一部分,编译器得知道确切字节数。如果你试图这样写:
int cap = 10; ArrayStack<int cap> s; // ❌ 编译失败</int>
会报错类似 ‘cap’ is not a constant expression。解决办法只有两个:
- 直接写死数字:
ArrayStack<int> s;</int> - 用
constexpr变量定义容量:constexpr size_t CAP = 16; ArrayStack<int cap> s;</int>
运行时动态容量?那就不是“基于数组”的栈了,得换 std::vector 或自己管理堆内存——那已经是另一个实现了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










