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