《拿下C++ 模板进阶!》:带你从模板分类与特点到实战的每一个细节!

《拿下C++ 模板进阶!》:带你从模板分类与特点到实战的每一个细节!

🔥个人主页:Cx330🌸

❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》

《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔

🌟心向往之行必能至


🎥Cx330🌸的简介:


目录

前言:

一、详解非类型模版参数

1.1  分类

1.2 演示

1.3 注意

二、模板的特化

2.1  模板特化概念

2.2  函数模板特化

2.2.1  函数模版的特化步骤

2.2.2  函数模板的特点

2.3 类模板特化

2.3.1 全特化

2.3.2 偏特化 / 半特化

2.3.3  类模板特化应用示例(一):排序特化比较器实现

2.3.4  类模板特化应用示例(二):结合优先级队列实现

2.4  偏特化的两种表现方式

2.4.1  部分特化

2.4.2  参数更进一步的限制

三、模版分离编译

3.1  分离编译的概念

3.2  模板的分离编译

3.2.1  模板分离代码简单演示

a.h:

a.cpp:

main.cpp:

3.2.2  分析运行过程

3.3  解决方法

四、模版总结

4.1  模版的优点

4.2  模版的缺陷

完整代码演示

一、模版进阶完整代码演示

priority_queue.h:

queue.h:

stack.h:

Test.cpp:

二、模版分离完整代码演示

 a.h:

a.cpp:

main.cpp:

结尾


前言:

之前,我们学习了模板初阶的内容,只是给大家分享了如何使用模板,而其中的细节将蕴含在这篇博客当中

一、详解非类型模版参数

1.1  分类

模板参数分类型形参与非类型形参

类型形参:即出现在模板参数列表中,跟在class或者typename之类的参数类型名称;

非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

1.2 演示

namespace bite { // 定义一个模板类型的静态数组 template<class T, size_t N = 10> class array { public: T& operator[](size_t index) { return _array[index]; } const T& operator[](size_t index)const { return _array[index]; } size_t size()const { return _size; } bool empty()const { return 0 == _size; } private: T _array[N]; size_t _size; }; }

1.3 注意

1、浮点数、类对象以及字符串是不允许作为非类型模板参数的。

2、非类型的模板参数必须在编译期就能确认结果。

二、模板的特化

2.1  模板特化概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些
错误的结果,
需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板

注:

Less据大多数情况下都可以进行比较,而遇到特殊情况,它就 “失效了” 例如:指针,你所要比较的是指针指向的内容,而Less比较的是指针,固然会 “失效”

此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方
式。
模板特化中分为函数模板特化类模板特化

2.2  函数模板特化

2.2.1  函数模版的特化步骤
1、必须要先有一个基础的函数模板

2、关键字template后面接一对空的尖括号<>

3、函数名后跟一对尖括号,尖括号中指定需要特化的类型;

4、函数形参表:必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
// 函数模板 -- 参数匹配 template<class T> bool Less(T left, T right) { return left < right; } // 对Less函数模板进行特化 template<> bool Less<Date*>(Date* left, Date* right) { return *left < *right; } int main() { cout << Less(1, 2) << endl; Date d1(2022, 7, 7); Date d2(2022, 7, 8); cout << Less(d1, d2) << endl; Date* p1 = &d1; Date* p2 = &d2; cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了 return 0; }
注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出
bool Less(Date* left, Date* right) { return *left < *right; }
该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化,优先使用重载而不是特化来处理特定类型
2.2.2  函数模板的特点

特点:如果说这里没有特化,就会实例化这个地方的原模版

2.3 类模板特化

2.3.1 全特化

全特化:将模板参数列表中所有的参数都确定化(所有模版都特化)

template<class T1, class T2> class Data { public: Data() { cout << "Data<T1, T2>" << endl; } private: T1 _d1; T2 _d2; }; template<> class Data<int, char> { public: Data() { cout << "Data<int, char>" << endl; } private: int _d1; char _d2; }; void TestVector() { Data<int, int> d1; Data<int, char> d2; }

如图:

2.3.2 偏特化 / 半特化

偏特化:也叫半特化,即任何针对模版参数进一步进行条件限制设计的特化版本(部分模版都特化)

template<class T1, class T2> class Data { public: Data() { cout << "Data<T1, T2>" << endl; } private: T1 _d1; T2 _d2; };

如图:

2.3.3  类模板特化应用示例(一):排序特化比较器实现

比如专门用来按照小于比较的类模板Less:

#include<vector> #include<algorithm> template<class T> struct Less { bool operator()(const T& x, const T& y) const { return x < y; } }; int main() { Date d1(2022, 7, 7); Date d2(2022, 7, 6); Date d3(2022, 7, 8); vector<Date> v1; v1.push_back(d1); v1.push_back(d2); v1.push_back(d3); // 可以直接排序,结果是日期升序 sort(v1.begin(), v1.end(), Less<Date>()); vector<Date*> v2; v2.push_back(&d1); v2.push_back(&d2); v2.push_back(&d3); // 可以直接排序,结果错误日期还不是升序,而v2中放的地址是升序 // 此处需要在排序过程中,让sort比较v2中存放地址指向的日期对象 // 但是走Less模板,sort在排序时实际比较的是v2中指针的地址,因此无法达到预期 sort(v2.begin(), v2.end(), Less<Date*>()); return 0; }
通过观察上述程序的结果发现,对于日期对象可以直接排序,并且结果是正确的。但是如果待排序元素是指针,结果就不一定正确(sort最终按照Less模板中方式比较,所以只会比较指针,而不是比较指针指向空间中内容),此时可以使用类版本特化来处理上述问题
// 对Less类模板按照指针方式特化 template<> struct Less<Date*> { bool operator()(Date* x, Date* y) const { return *x < *y; } };
2.3.4  类模板特化应用示例(二):结合优先级队列实现

运行结果:

2.4  偏特化的两种表现方式

2.4.1  部分特化
// 将第二个参数特化为int template <class T1> class Data<T1, int> { public: Data() { cout << "Data<T1, int>" << endl; } private: T1 _d1; int _d2; };

如图:

2.4.2  参数更进一步的限制
//两个参数偏特化为指针类型 template <typename T1, typename T2> class Data <T1*, T2*> { public: Data() { cout << "Data<T1*, T2*>" << endl; } private: T1 _d1; T2 _d2; }; //两个参数偏特化为引用类型 template <typename T1, typename T2> class Data <T1&, T2&> { public: Data(const T1& d1, const T2& d2) : _d1(d1) , _d2(d2) { cout << "Data<T1&, T2&>" << endl; } private: const T1& _d1; const T2& _d2; }; void test2() { Data<double, int> d1; // 调用特化的int版本 Data<int, double> d2; // 调用基础的模板 Data<int*, int*> d3; // 调用特化的指针版本 Data<int&, int&> d4(1, 2); // 调用特化的指针版本 }

如图:

#pragma once template<class T> T TAdd(const T& left, const T& right) { return left + right; } int Add(int x, int y);

三、模版分离编译

3.1  分离编译的概念

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件(.o),最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式

3.2  模板的分离编译

3.2.1  模板分离代码简单演示
a.h:
#pragma once template<class T> T TAdd(const T& left, const T& right) { return left + right; } int Add(int x, int y);
a.cpp:
#define _CRT_SECURE_NO_WARNINGS 1 #include"a.h" //// 预处理:展开头文件 //template<class T> //T TAdd(const T& left, const T& right) //{ // return left + right; //} int Add(int x, int y) { return x + y; } //// 不用< > //template //double TAdd(const double& left, const double& right); template int TAdd(const int& left, const int& right);
main.cpp:
#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; #include"a.h" int main() { cout << Add(1, 2) << endl; cout << TAdd(1.1, 2.2) << endl; cout << TAdd(1, 2) << endl; return 0; }
3.2.2  分析运行过程

运行结果:

3.3  解决方法

1、将声明和定义放到一个文件“xxx.hpp"里面或者xxx.h其实也是可以的。推荐使用这种。
2、模板定义的位置显式实例化。这种方法不实用,不推荐使用

四、模版总结

4.1  模版的优点

1、模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生;
2、增强了代码的灵活性

4.2  模版的缺陷

1、模板会导致代码膨胀问题,也会导致编译时间变长;
2、出现模板编译错误时,错误信息非常凌乱,不易定位错误

完整代码演示

一、模版进阶完整代码演示

priority_queue.h:
#pragma once #include<vector> namespace bit { template <class T> struct Less { bool operator() (const T& x, const T& y) const { return x < y; } }; template <class T> struct Greater { bool operator() (const T& x, const T& y) const { return x > y; } }; //默认大的优先级高 template<class T, class Container = std::vector<T>,class Compare = Less<T>> class priority_queue { public: template <class InputIterator> priority_queue(InputIterator first, InputIterator last) :_con(first,last) { //建堆,向下调整建堆,从最后一个父节点开始向下调整 for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--) { adjust_down(i); } } //强制编译器生成默认构造 priority_queue() = default; void adjust_up(int child) { Compare com; int parent = (child - 1) / 2; while (child > 0) { //if (_con[child] > _con[parent]) //if (_con[parent] < _con[child]) if (com(_con[parent],_con[child])) { swap(_con[child], _con[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } } void adjust_down(int parent) { Compare com; int child = 2 * parent; while (child < _con.size()) { //if (child+1 < _con.size() && _con[child + 1] > _con[child]) //if (child+1 < _con.size() && _con[child] < _con[child + 1]) if (child + 1 < _con.size() && com(_con[child], _con[child + 1])) { ++child; } //if (_con[child] > _con[parent]) if (com(_con[parent], _con[child])) { swap(_con[child], _con[parent]); parent = child; child = 2 * parent; } else { break; } } } void push(const T& x) { _con.push_back(x); adjust_up(_con.size() - 1); } void pop() { swap(_con[0], _con[_con.size() - 1]); _con.pop_back(); adjust_down(0); } const T& top() const { return _con[0]; } bool empty() const { return _con.empty(); } size_t size() const { return _con.size(); } private: Container _con; }; }
queue.h:
#pragma once #include<vector> #include<list> #include<deque> namespace bit { //template<class T> //class stack //{ //private: // T* _a; // size_t _top; // size_t _capacity; //}; //容器适配器 template<class T, class Container = deque<T>> class queue { public: void push(const T& x) { _con.push_back(x); } void pop() { _con.pop_front(); } const T& front() { return _con.front(); } const T& back() { return _con.back(); } size_t size() const { return _con.size(); } bool empty() const { return _con.empty(); } private: Container _con; }; }
stack.h:
#pragma once #include<vector> #include<list> namespace bit { //template<class T> //class stack //{ //private: // T* _a; // size_t _top; // size_t _capacity; //}; //容器适配器 template<class T,class Container> class stack { public: void push(const T& x) { _con.push_back(x); } void pop() { _con.pop_back(); } const T& top() { return _con.back(); } size_t size() const { return _con.size(); } bool empty() const { return _con.empty(); } private: Container _con; }; }
Test.cpp:
#include<iostream> #include<stack> #include<queue> #include<algorithm> #include<list> using namespace std; //int main() //{ //stack<int> st; //st.push(1); //st.push(2); //st.push(3); //st.emplace(4); //while (!st.empty()) //{ // cout << st.top() << ' '; // st.pop(); //} //cout << endl; // queue<int> q; // q.push(1); // q.push(2); // q.push(3); // q.emplace(4); // while (!q.empty()) // { // cout << q.front() << ' '; // q.pop(); // } // cout << endl; // return 0; //} //#include"stack.h" //int main() //{ // //bit::stack<int, vector<int>> st;//数组栈 // bit::stack<int,list<int>> st;//链式栈 // st.push(1); // st.push(2); // st.push(3); // st.push(4); // while (!st.empty()) // { // cout << st.top() << ' '; // st.pop(); // } // cout << endl; // return 0; //} //#include"queue.h" //#include<deque> //int main() //{ // //bit::queue<int> q; // //bit::queue<int,vector<int>> q; // bit::queue<int, list<int>> q; // // q.push(1); // q.push(2); // q.push(3); // q.push(4); // // while (!q.empty()) // { // cout << q.front() << ' '; // q.pop(); // } // cout << endl; // // deque<int> dq; // dq.push_back(1); // dq.push_back(1); // dq.push_back(1); // dq.push_front(2); // dq.push_front(3); // dq.push_front(4); // dq[0] += 10; // for (auto e : dq) // { // cout << e<<' '; // } // cout << endl; // return 0; //} //void test_op1() //{ // srand(time(0)); // const int N = 1000000; // // deque<int> dq; // vector<int> v; // for (int i = 0; i < N; ++i) // { // auto e = rand() + i; // v.push_back(e); // dq.push_back(e); // } // // int begin1 = clock(); // sort(v.begin(), v.end()); // int end1 = clock(); // // int begin2 = clock(); // sort(dq.begin(), dq.end()); // int end2 = clock(); // // printf("vector:%d\n", end1 - begin1); // printf("deque:%d\n", end2 - begin2); //} // //void test_op2() //{ // srand(time(0)); // const int N = 1000000; // // deque<int> dq1; // deque<int> dq2; // // for (int i = 0; i < N; ++i) // { // auto e = rand() + i; // dq1.push_back(e); // dq2.push_back(e); // } // // int begin1 = clock(); // sort(dq1.begin(), dq1.end()); // int end1 = clock(); // // int begin2 = clock(); // // 拷贝到vector // vector<int> v(dq2.begin(), dq2.end()); // sort(v.begin(), v.end()); // dq2.assign(v.begin(), v.end()); // int end2 = clock(); // // printf("deque sort:%d\n", end1 - begin1); // printf("deque copy vector sort, copy back deque:%d\n", end2 - begin2); //} //int main() //{ // test_op2(); // return 0; //} #include<queue> //仿函数 //template <class T> //struct Less{ // bool operator() (const T& x, const T& y) const { return x < y; } //}; //int main() //{ // //priority_queue<int> pq;//默认是大的数优先级高(大堆) // priority_queue<int,vector<int>,greater<int>> pq;//调整小的优先级高 // pq.push(3); // pq.push(1); // pq.push(5); // pq.push(7); // pq.push(2); // while (!pq.empty()) // { // cout << pq.top() << ' '; // pq.pop(); // } // cout << endl; // // //Less<int> less; // //cout << less(1, 2) << endl; // //cout << less.operator()(1, 2) << endl; // // return 0; //} #include"priority_queue.h" //int main() //{ // //bit::priority_queue<int> pq; // int a[] = { 30,4,2,66,3 }; // //bit::priority_queue<int> pq(a, a+5); // bit::priority_queue<int,vector<int>,bit::Greater<int>> pq(a, a + 5); // pq.push(3); // pq.push(1); // pq.push(5); // pq.push(7); // pq.push(2); // // while (!pq.empty()) // { // cout << pq.top() << ' '; // pq.pop(); // } // cout << endl; // // return 0; //} class Date { public: Date(int year = 1900, int month = 1, int day = 1) :_year(year) ,_month(month) ,_day(day) { } bool operator<(const Date& d) const { return (_year < d._year) || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day < d._day); } bool operator>(const Date& d) const { return (_year > d._year) || (_year == d._year && _month > d._month) || (_year == d._year && _month == d._month && _day > d._day); } friend ostream& operator<<(ostream& _cout, const Date& d); private: int _year; int _month; int _day; }; ostream& operator<<(ostream& _cout, const Date& d) { _cout << d._year << "-" << d._month << "-" << d._day; return _cout; } //struct PDateLess //{ // bool operator()(const Date* p1, const Date* p2) // { // return *p1 < *p2; // } //}; // //struct DelEven //{ // bool operator()(int x) // { // return x % 2 == 0;//偶数为真 // } //}; // //int main() //{ // bit::priority_queue<Date*,vector<Date*>,PDateLess> pq; // pq.push(new Date(2025, 1, 1)); // pq.push(new Date(2025, 1, 3)); // pq.push(new Date(2025, 1, 2)); // while (!pq.empty()) // { // cout << *pq.top() << ' '; // pq.pop(); // } // cout << endl; // // vector<int> v1 = { 1,6,1,7,3,8,9,3 }; // // < 升序 // // > 降序 // //仿函数 // //greater<int> gt;//降序 // sort(v1.begin(), v1.end(),greater<int>()); // for (auto e : v1) // { // cout << e << " "; // } // cout << endl; // // list<int> lt1 = { 1,2,3,4,5,6,7 }; // //基于某个条件删除,条件由仿函数控制 // //比如删除所有偶数 // lt1.remove_if(DelEven()); // for (auto e : lt1) // { // cout << e << " "; // } // cout << endl; // // return 0; //} ////////////////////////////////////////////////////////////////////////////////////////////// //模板进阶 //template<class T,size_t N> //class Stack //{ //private: // T _a[N]; // int _top; //}; ////C++20才开始支持 ////template<double N> ////class AA ////{ }; // ////不支持 ////template<string str> ////支持 // //template<int*ptr> //class BB //{ }; // //#include<array> // //void func(int* a,int n) //{ // //不能使用范围for //} //void func(array<int, 10>& a) //{ // //能使用范围for // for (auto e : a) // { // cout << e << " "; // } // cout << endl; //} // //int main() //{ // Stack<int, 10> st1;//10个空间 // Stack<int, 1000> st2;//1000 // // //内置类型做出参数默认不会初始化 // array<int, 10> a1; // a1.fill(0); // a1[3] = 3; // a1[9] = 9; // for (auto e : a1) // { // cout << e << " "; // } // cout << endl; // cout << sizeof(a1) << endl; // // int a2[10]; // a2[3] = 3; // a2[9] = 9; // for (auto e : a2) // { // cout << e << " "; // } // cout << endl; // //再去做其他容器的类型,或者传参,array都有普通数组达不到的优势 // list<array<int, 10>> lt1; // func(a1); // func(a2,10); // //数组越界只能检查写,而且是抽查 // //a2[10] = 1;//可以查出来 // //a2[15] = 1;//远一点的就查不出来 // cout << a2[10] << endl;//越界读是一点办法没有 // // //上面对array都不是问题,都可以检查出来,因为他是运算符重载调用,内部严格检查 // a1[15] = 1; // cout << a1[10] << endl; // // return 0; //} //函数模板特化 //template<class T> //bool Less(T left, T right) //{ // return *left < *right; //} // ////对上述函数模板实现一个特化版本 ////特化:针对某些类型进行特殊化处理 //template<> //bool Less<Date*>(Date* left, Date* right) //{ // return left < right; //} // //int main() //{ // cout << Less(1, 2) << endl; // Date* p1 = new Date(2025, 1, 1); // Date* p2 = new Date(2025, 1, 3); // cout << Less(p1, p2) << endl;//可以比较,结果错误 // // return 0; //} //特化版本形参结构跟原模板必须保持一致,比如说原模版是const的形参,特化版本也必须是 //对上述函数模板实现一个特化版本 //特化:针对某些类型进行特殊化处理 //T* const p;//const修饰指针本身 //const T* p;//const修饰指针指向内容 //template<class T> //bool Less(const T& left, const T& right)//const修饰引用 //{ // return left < right; //} //template<> ////bool Less<Date*>(const Date*& left, const Date*& right)//const修饰指针指向内容 //bool Less<Date*>(Date* const & left,Date* const & right) //{ // return left < right; //} // //int main() //{ // cout << Less(1, 2) << endl; // Date* p1 = new Date(2025, 1, 1); // Date* p2 = new Date(2025, 1, 3); // cout << Less(p1, p2) << endl; // // return 0; //} template<class T> bool Less(const T& left, const T& right)//const修饰引用 { return left < right; } bool Less(Date* left, Date* right)//const修饰引用 { return *left < *right; } //int main() //{ // cout << Less(1, 2) << endl;//可以比较,结果正确 // Date* p1 = new Date(2025, 1, 1); // Date* p2 = new Date(2025, 1, 3); // cout << Less(p1, p2) << endl;//可以比较,结果错误 // // return 0; //} //类模板特化 template<class T1, class T2> class Data { public: Data() { cout << "Data<T1, T2>" << endl; } private: T1 _d1; T2 _d2; }; ////类模板特化,对内部成员没有要求,也就是说原模版定义的,特化版本可以不定义,也可以新增 //全特化 //template<> //class Data<int,double> //{ //public: // Data() { cout << "Data<int, double> 特化" << endl; } // void func(){ } //}; //偏特化/半特化 //template<class T1> //class Data<T1,double> //{ //public: // Data() { cout << "Data<T1, char> 特化" << endl; } //}; //限制实例化参数是指针 template<class T1, class T2> class Data<T1*, T2*> { public: Data() { cout << "Data<T1*, T2*> 特化" << endl; } void func() { cout << typeid(T1).name() << endl; cout << typeid(T2).name() << endl; } }; //引用 template<class T1, class T2> class Data<T1&, T2&> { public: Data() { cout << "Data<T1&, T2&> 特化" << endl; } void func() { cout << typeid(T1).name() << endl; cout << typeid(T2).name() << endl; } }; //混合 template<class T1> class Data<T1*,int> { public: Data() { cout << "Data<T1*,int> 特化" << endl; } void func() { cout << typeid(T1).name() << endl; } }; //int main() //{ // Data<int, int> d1; // //d1.func(); // // Data<int, double> d2; // d2.func(); // // Data<char, double> d3; // // Data<char*, double*> d4; // d4.func(); // // Data<char&, double&> d5; // d5.func(); // // Data<char*, int> d6; // d6.func(); // // return 0; //} namespace bit { //特化版本 template <> struct Less<Date*> { //bool operator() (const Date* const & x, const Date* const & y) const bool operator() (const Date* x, const Date* y) const { return *x < *y; } }; //偏特化,所有的指针都按照指向的内容去比较 template <class T> struct Less<T*> { bool operator() (const T* x, const T* y) const { return *x < *y; } }; template <> struct Greater<Date*> { bool operator() (const Date* const & x, const Date* const & y) const { return *x > *y; } }; } //int main() //{ // bit::priority_queue<Date*> pq; // //bit::priority_queue<Date*,vector<Date*>,bit::Greater<Date*>> pq; // // pq.push(new Date(2025, 1, 1)); // pq.push(new Date(2025, 1, 3)); // pq.push(new Date(2025, 1, 2)); // while (!pq.empty()) // { // cout << *pq.top() << ' '; // pq.pop(); // } // cout << endl; // // return 0; //}

二、模版分离完整代码演示

 a.h:
#pragma once template<class T> T TAdd(const T& left, const T& right) { return left + right; } int Add(int x, int y);
a.cpp:
#include"a.h" //template<class T> //T TAdd(const T& left, const T& right) //{ // return left + right; //} int Add(int x, int y) { return x + y; } //显式实例化 //template //double TAdd(const double& left, const double& right); // //template //int TAdd(const int& left, const int& right);
main.cpp:
#include<iostream> using namespace std; #include"a.h" int main() { cout<< Add(1, 2) << endl; cout << TAdd(1.1, 2.2) << endl; cout << TAdd(1, 2) << endl; return 0; }

结尾

往期回顾:

《C++ Stack与Queue详解掌握指南》:带领你从基础夯实到玩转栈与队列容器

Read more

华为OD机试双机位C卷 - 部门人力分配 (C++ & Python & JAVA & JS & GO)

华为OD机试双机位C卷 - 部门人力分配 (C++ & Python & JAVA & JS & GO)

部门人力分配 华为OD机试双机位C卷 - 华为OD上机考试双机位C卷 100分题型 华为OD机试双机位C卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解 题目描述 部门在进行需求开发时需要进行人力安排。 当前部门需要完成 N 个需求,需求用 requirements 表述,requirements[i] 表示第 i 个需求的工作量大小,单位:人月。 这部分需求需要在 M 个月内完成开发,进行人力安排后每个月人力时固定的。 目前要求每个月最多有2个需求开发,并且每个月需要完成的需求不能超过部门人力。 请帮助部门评估在满足需求开发进度的情况下,每个月需要的最小人力是多少? 输入描述 输入为 M 和 requirements,M 表示需求开发时间要求,requirements 表示每个需求工作量大小,N 为 requirements长度, * 1 ≤ N/2 ≤ M ≤ N ≤ 10000

By Ne0inhk

Java助力:开启旅游系统个性化畅享之旅

Java助力:开启旅游系统个性化畅享之旅 在旅游行业数字化转型的浪潮中,个性化服务已成为提升用户体验、增强竞争力的核心要素。Java凭借其强大的跨平台能力、高并发处理能力以及丰富的生态系统,成为构建个性化旅游系统的理想选择。通过整合大数据、人工智能、机器学习等技术,Java赋能的旅游系统能够精准捕捉用户需求,提供从行程规划到旅途体验的全流程个性化服务,让每一次出行都成为独一无二的畅享之旅。 一、Java技术优势:支撑个性化旅游系统的基石 1. 跨平台与可扩展性 Java“一次编写,到处运行”的特性,使旅游系统能够无缝适配Web、移动端(Android/iOS)、小程序等多终端,满足用户随时随地访问的需求。同时,Java的模块化设计和微服务架构(如Spring Cloud)支持系统灵活扩展,轻松应对高并发场景。例如,在节假日旅游高峰期,系统可通过动态扩容订单服务、推荐服务等模块,确保流畅运行。 2. 高性能与稳定性 Java的JVM优化和垃圾回收机制,结合分布式缓存(如Redis)、消息队列(如Kafka)等技术,能够高效处理海量数据,保障系统稳定性。

By Ne0inhk
【C++笔记】STL详解:String类的实现

【C++笔记】STL详解:String类的实现

前言:                 在前面的学习中,我们已经初步掌握了string类接口函数的使用方法,本文将带领大家从零开始,逐步实现一个完整的string类。          一、string类总览                 温馨提示: 为了避免与标准库中的string产生命名冲突,我们使用mystd命名空间进行封装。 namespace mystd { class string { public: //迭代器 typedef char* iterator; typedef const char* const_iterator; //默认成员函数 string(); string(const char* str); //构造函数 string(const string& s); //拷贝构造函数 string& operator=(const string& s); //赋值运算符重载函数 ~string(); //析构函数 //迭代器相关函数 iterator begin(

By Ne0inhk