上篇讲了模板函数,今天进一步——模板类。
模板类在面试里的出镜率比模板函数高得多。因为STL容器全是模板类实现的,面试官问你vector、map、unordered_map的底层原理,本质上都在考模板类的设计思路。
讲个面试场景。
面试官问:"你用过vector吧?vector、vector、vector都能用,它底层是怎么做到的?"
如果你回答"模板",面试官会点头,然后追问:"那你手写一个简单的模板类试试?"
很多人就卡住了。
今天把模板类从语法到实战一次讲透。
模板类的基本语法
手写一个简单的动态数组:
template<typename T> class SimpleVector { T* data_; int size_; int capacity_; public: SimpleVector() : data_(nullptr), size_(0), capacity_(0) {} void push_back(const T& value) { if (size_ == capacity_) { int new_cap = capacity_ == 0 ? 1 : capacity_ * 2; T* new_data = new T[new_cap]; for (int i = 0; i < size_; i++) { new_data[i] = data_[i]; } delete[] data_; data_ = new_data; capacity_ = new_cap; } data_[size_++] = value; } T& operator[](int index) { return data_[index]; } int size() const { return size_; } ~SimpleVector() { delete[] data_; } };用法和std::vector一样:
SimpleVector<int> nums; nums.push_back(1); nums.push_back(2); nums.push_back(3); cout << nums[0] << endl; // 输出: 1 SimpleVector<double> values; values.push_back(3.14);注意一个关键点:模板类的成员函数定义通常也放在头文件里,和模板函数一样。因为编译器在实例化时需要看到完整定义。
模板类的实例化机制
模板类跟普通类最大的区别是:模板类本身不是一个"真正的类",它只是一个生成类的"蓝图"。
当你写SimpleVector<int>的时候,编译器把蓝图里所有的T替换成int,生成一个具体的类。这个过程叫模板实例化。
SimpleVector<int> → 编译器生成 SimpleVector_int(所有T换成int) SimpleVector<double> → 编译器生成 SimpleVector_double(所有T换成double)每个不同的类型参数,编译器都会生成一份独立的代码。所以SimpleVector<int>和SimpleVector<double>是两个完全不同的类,它们之间没有任何关系。
这一点很重要。你不能把SimpleVector<int>*赋值给SimpleVector<double>*,虽然它们来自同一个模板,但实例化后就是两个独立的类型。
在机器人开发里怎么用模板类
在机器人开发里,模板类最常见的应用是数据结构。
比如你需要一个点云数据的容器,不同传感器返回的点类型不一样:
template<typename PointT> class PointCloud { vector<PointT> points_; string frame_id_; uint64_t timestamp_; public: void addPoint(const PointT& p) { points_.push_back(p); } size_t size() const { return points_.size(); } const PointT& operator[](size_t i) const { return points_[i]; } void setFrameId(const string& id) { frame_id_ = id; } }; struct PointXYZ { float x, y, z; }; struct PointXYZRGB { float x, y, z; uint8_t r, g, b; }; PointCloud<PointXYZ> cloud1; PointCloud<PointXYZRGB> cloud2;PCL(Point Cloud Library)就是这么设计的。如果你用过PCL,pcl::PointCloud<pcl::PointXYZ>就是模板类的典型用法。
类模板的特化
和模板函数一样,模板类也可以特化——为特定类型提供不同的实现。
// 通用版本 template<typename T> class DataParser { public: T parse(const string& data) { // 通用解析逻辑 return T(); } }; // string的特化版本 template<> class DataParser<string> { public: string parse(const string& data) { // string不需要解析,直接返回 return data; } }; // int的特化版本 template<> class DataParser<int> { public: int parse(const string& data) { return stoi(data); } };在机器人开发里,传感器数据的解析就经常需要特化。不同传感器的数据格式不一样,用模板特化可以为每种数据类型提供专门的解析逻辑,同时保持统一的接口。
面试中的模板类考点
面试官考模板类,有几个高频问题。
第一个:"模板类和普通类有什么区别?"核心回答:模板类是类的蓝图,需要实例化后才能使用;每个实例化版本是独立的类;定义通常要放在头文件里。
第二个:"模板类的静态成员变量怎么工作?"每个实例化版本有自己独立的静态成员。SimpleVector<int>::count和SimpleVector<double>::count是两个不同的变量。
template<typename T> class Counter { public: static int count; }; template<typename T> int Counter<T>::count = 0; Counter<int>::count = 10; Counter<double>::count = 20; // 两个count互不影响第三个:"模板类能不能有非模板成员?"可以。模板类里可以有普通成员函数和普通成员变量,它们不受模板参数影响。
再聊一个面试高频追问:模板类的全特化和偏特化有什么区别?全特化是针对所有模板参数都指定了具体类型,比如template<> class SimpleVector<bool>,为bool类型做一个特殊版本。偏特化是只指定部分参数,比如template<typename T> class SimpleVector<T*>,专门处理指针类型。偏特化在实际项目中用得比全特化更多,比如STL的std::iterator_traits就大量使用了偏特化来提取不同类型迭代器的特征。面试时如果你能举出偏特化的实际应用例子,而不是只停留在语法层面,面试官会认为你对模板编程有比较深入的理解,而不只是会背概念。
给正在准备面试的你一点建议
模板类在面试里考得比模板函数多,因为STL容器就是模板类。
最基础的要求:能手写一个简单的模板类,理解实例化的概念。
进阶要求:知道模板特化,能解释为什么模板定义要放头文件。
如果你能把vector的简化版写出来(就像今天这个SimpleVector),面试官对你的评价会很高。因为这说明你不仅会用STL,还理解它的设计思路。
下篇讲异常处理try/catch——机器人系统的容错基础。从C++基础语法进入工程实践的领域了。
如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。
「机器人软件开发面试·从入门到精通」连载系列上一篇:第26篇 模板函数——写通用算法的利器 下一篇预告:第28篇 异常处理try/catch——机器人系统的容错基础
有任何问题欢迎评论区留言,我会尽量回复。