C++:如何从模板类派生并重载构造函数?及自定义vector对象计数
解决你的vector模板类需求:别名、对象计数与派生重载
1. 给vector<double,2>指定类型别名
给模板特化类型起别名非常简单,C里有两种常用方式,其中C11及以后推荐的using语法更直观,尤其适合复杂模板场景:
// 方式1:传统typedef typedef vector<double, 2> Vec2d; // 方式2:using(更推荐,可读性更强) using Vec2d = vector<double, 2>;
之后你就可以用Vec2d代替冗长的vector<double,2>声明变量,比如Vec2d my_vec;。
2. 统计对象总数与存活数
要统计特定模板特化(比如Vec2d)的对象总数和存活数,我们需要在模板类中添加静态成员变量——每个模板特化都是独立类型,它们的静态成员会各自维护独立的计数。
修改你的模板类,添加静态成员并在构造/析构中更新计数:
#include <iostream> // 注意把size改成size_t,和标准库的类型规范保持一致 template<typename T, size_t S> class vector{ protected: // 改成protected方便派生类访问(如果需要的话) T *_values; private: // 静态成员:统计当前类型的总创建数和存活数 static size_t total_objects; static size_t live_objects; public: // 简化默认构造:用new T[S]()直接完成值初始化 vector(): _values(new T[S]()){ total_objects++; live_objects++; } // 拷贝构造也要更新计数(如果需要支持拷贝) vector(const vector& other): _values(new T[S]){ for(size_t i = 0; i < S; i++){ _values[i] = other._values[i]; } total_objects++; live_objects++; } // 移动构造同理,避免空指针重复delete vector(vector&& other) noexcept: _values(other._values){ other._values = nullptr; total_objects++; live_objects++; } ~vector(){ if(_values != nullptr){ delete[] _values; live_objects--; } } // 封装静态计数的获取方法,比直接访问成员更符合封装原则 static size_t get_total_count(){ return total_objects; } static size_t get_live_count(){ return live_objects; } }; // C++17前必须在类外定义模板的静态成员,C++17后可使用inline静态成员省略这一步 template<typename T, size_t S> size_t vector<T, S>::total_objects = 0; template<typename T, size_t S> size_t vector<T, S>::live_objects = 0;
测试Vec2d的计数效果:
int main(){ using Vec2d = vector<double, 2>; std::cout << "Total: " << Vec2d::get_total_count() << ", Live: " << Vec2d::get_live_count() << std::endl; // 输出 0,0 Vec2d v1; std::cout << "Total: " << Vec2d::get_total_count() << ", Live: " << Vec2d::get_live_count() << std::endl; // 输出 1,1 { Vec2d v2; std::cout << "Total: " << Vec2d::get_total_count() << ", Live: " << Vec2d::get_live_count() << std::endl; // 输出 2,2 } // v2超出作用域析构,存活数减1 std::cout << "Total: " << Vec2d::get_total_count() << ", Live: " << Vec2d::get_live_count() << std::endl; // 输出 2,1 return 0; }
⚠️注意:如果你的类需要支持拷贝、移动操作,一定要在对应的构造函数中更新计数,否则统计结果会不准确。
3. 从模板类派生并重载构造函数
从模板类派生的核心是明确基类的模板参数,同时在派生类构造函数中正确调用基类构造。比如我们派生一个支持初始化列表的MyVector:
#include <initializer_list> #include <stdexcept> template<typename T, size_t S> class MyVector : public vector<T, S>{ public: // 重载构造:用初始化列表初始化元素 MyVector(std::initializer_list<T> init) : vector<T, S>(){ // 先调用基类默认构造 if(init.size() != S){ throw std::invalid_argument("初始化列表长度和vector大小不匹配!"); } size_t i = 0; for(const auto& val : init){ this->_values[i++] = val; } } // 重载构造:用单个值初始化所有元素 MyVector(const T& val) : vector<T, S>(){ for(size_t i = 0; i < S; i++){ this->_values[i] = val; } } };
使用这个派生类:
int main(){ MyVector<double, 2> v({1.5, 3.2}); // 用初始化列表构造二维向量 MyVector<int, 3> v2(7); // 三维向量所有元素初始化为7 return 0; }
如果不想把基类的_values改成protected,也可以在基类中添加公有的元素设置方法(比如void set(size_t idx, const T& val)),这样更符合封装的设计原则。
内容的提问来源于stack exchange,提问作者pjoter
相关产品推荐
相关产品推荐

