C++笔记(9) 模板,向量和栈
函数模板,类模板
模板功能提供了在函数和类中将类型作为参数的能力,可以设计具有通用类型的函数和类,而编译器在编译时会将通用类型确定为具体类型。
如max()函数:需要比较两个int,double, char, string,需要写四个重载函数
C++允许定义具有通用类型的函数模板
#include <iostream> #include <iomanip> #include <cstdlib> #include <time.h> #include <string> using namespace std; // 定义函数模板 template<typename T> T maxValue(T a, T b) { if(a>=b) return a; else return b; } int main(int argc, char *argv[]) { cout << "Maxium between 1 and 3 is " << maxValue(1,3) << endl; cout << "Maxium between 1.6 and 0.3 is " << maxValue(1.6,0.3) << endl; cout << "Maxium between a and c is " << maxValue('a','c') << endl; cout << "Maxium between \"NFC\" and \"NBA\" is " << maxValue(string("NFC"), string("NBA")) << endl; return 0; }
关键字:template, 后面是参数列表,参数列表必须有关键字typename
在进行string类型的比较时,如果使用maxValue("NFC", "NBA")的话,参数为C字符串,传递给函数的参数是两个字符串的地址,所以比较的是地址。
例子:一个通用的排序函数:
#include <iostream> #include <iomanip> #include <cstdlib> #include <time.h> #include <string> using namespace std; // 定义函数模板 一个通用的排序函数 template<typename T> void sortedArray(T array[], int SIZE) { // 选择排序 for(int i=0; i<SIZE-1; i++) { T current_min = array[i]; int current_min_index = i; for(int j=i+1; j<SIZE; j++) { if(array[j]<current_min) { current_min = array[j]; current_min_index = j; } } if(current_min_index!=i) { T tmp = array[i]; array[i] = array[current_min_index]; array[current_min_index] = tmp; } } } template<typename T> void printArray(T array[], int SIZE) { for(int i=0; i<SIZE; i++) { cout << setw(5) << array[i]; } cout << endl; } int main(int argc, char *argv[]) { int a[] = {3, 5, 2, 6, 9, 4}; sortedArray(a, 6); printArray(a, 6); double b[] = {6.3, 4.1, 4.2, 7.9, 0.6, 8.8}; sortedArray(b, 6); printArray(b, 6); string c[] = {"lanzhou", "student", "wangzi"}; sortedArray(c, 3); printArray(c, 3); return 0; }
模板类:
除了用类型参数定义模板函数,也可以用类型参数定义模板类,类型参数可以用于类中任何地方
将StackOfInteger修改为通用 的Stack类: // 对于模板类,将类的定义和实现放在一起更为安全,有的编译器不支持将类的定义和实现分离!
声明模板类对象时,必须指定函数类型
模板类与普通类区别总结:
1. 定义和实现放在一起 h文件
2.类的定义,前面必须有模板前缀 template<typename T>
3.类的实现中每个构造函数和成员函数之前都要有模板前缀 template<typename T>, 类的名称为ClassName<T>::methods
4.声明类的对象时,必须指定参数类型 Class<int/string> object
修改intStack为模板类:
code:
stack.h
// stack 类的定义 #ifndef STACK_H #define STACK_H using namespace std; template<typename T> class Stack { private: int size; T element[100]; public: Stack(); // 构造函数 bool isEmpty() const; // 只读函数 T peek() const; //返回栈底的元素 void push(T value); // 入栈 T pop(); // 出栈 int getSize() const; }; // 类的实现 // 类的构造函数和成员函数的实现与模板函数一样,前面要模板前缀,同时类名为Stack<T> template<typename T> Stack<T>::Stack() { size = 0; } template<typename T> bool Stack<T>::isEmpty() const { return size==0; } template<typename T> T Stack<T>::peek() const { return element[size-1]; // 只读函数不能改变数据域 } template<typename T> void Stack<T>::push(T value) { element[size++] = value; } template<typename T> T Stack<T>::pop() { if(isEmpty()) cout << "The stack is stack" << endl; else return element[--size]; } template<typename T> int Stack<T>::getSize() const { return size; } #endif
main.cpp
#include <iostream> #include <string> #include <iomanip> #include <sstream> // 将数字转化为字符串 #include <algorithm> #include "E:\back_up\code\c_plus_code\chapter8\external_file\course.h" #include "E:\back_up\code\c_plus_code\chapter8\external_file\stack.h" using namespace std; int main(int argc, char *argv[]) { Stack<int> int_stack; //声明模板类对象。必须指定类型 for(int i=0; i< 10; i++) { int_stack.push(i); } cout << "Peek int_stack " << int_stack.peek() << endl; while(!int_stack.isEmpty()) { cout << int_stack.pop() << " "; } cout << endl; Stack<string> string_stack; string_stack.push("Nanhudadao"); string_stack.push("Helloworld"); string_stack.push("stongarm"); while(!string_stack.isEmpty()) { cout << string_stack.pop() << " "; } cout << endl; return 0; }
可以定义一个输出栈中元素的函数模板,函数参数是栈的引用:
#include <iostream> #include <string> #include <iomanip> #include <sstream> // 将数字转化为字符串 #include <algorithm> #include "E:\back_up\code\c_plus_code\chapter8\external_file\course.h" #include "E:\back_up\code\c_plus_code\chapter8\external_file\stack.h" using namespace std; // 定义一个输出栈中元素的函数 template<typename T> void printStack(Stack<T>& stack) // 参数 { while(!stack.isEmpty()) { cout << stack.pop() << " "; } cout << endl; } int main(int argc, char *argv[]) { Stack<int> int_stack; //声明模板类对象。必须指定类型 for(int i=0; i< 10; i++) { int_stack.push(i); } cout << "Peek int_stack " << int_stack.peek() << endl; printStack(int_stack); Stack<string> string_stack; string_stack.push("Nanhudadao"); string_stack.push("Helloworld"); string_stack.push("stongarm"); printStack(string_stack); return 0; }
可以为模板类中的类型参数指定一个默认的参数类型作为缺省类型,还可以使用非类型参数。
template<typename T=int, int capacity> // 默认的参数类型是int class Stack { private: T element[capacity]; int size; public: .... } void main() { Stack<int, 500> int_stack; // 500哥元素的栈 }
Stack<> stack;
Stack类的改进:
前面的Stack中数据存储在一个固定大小的数组中,不合理。
改进方法:预先分配一个较小的空间,如果需要,再动态增加数组的大小
思路: 增加一个新的属性capacity,表示保存元素的数组的当前大小,构造函数中先创建一个小的数组,当数组已满时,就增加数组的大小来保存新的元素:
如何增加数组的大小呢?
创建一个新的更大的数组,和将原数组中的元素复制过来,将旧的数组删掉即可。
实现代码
stack_improve.h
#ifndef STACK_IMPRPVE_H #define STACK_IMPROVE_H #include <iostream> using namespace std; template<typename T=int> class Stack_improve { private: T* elements; int size; int capacity; void ensureCapacity(); public: Stack_improve(); // 构造函数 Stack_improve(const Stack_improve&); // 拷贝构造函数 ~Stack_improve(); // 析构函数 bool isEmpty() const; T peek() const; void push(T value); T pop(); int getSize() const; }; template<typename T> Stack_improve<T>::Stack_improve() { size = 0; // 元素个数 capacity = 10; // 栈的初始容量的大小为10 elements = new T[capacity]; } template<typename T> Stack_improve<T>::Stack_improve(const Stack_improve& stack) // 拷贝构造函数,实现深拷贝 { elements = new T[stack.capacity]; // 分配独立的存储空间 size = stack.size; capacity = stack.capacity; for(int i=0; i<size; i++) { elements[i] = stack.elements[i]; } } template<typename T> Stack_improve<T>::~Stack_improve() { delete []elements; } template<typename T> bool Stack_improve<T>::isEmpty() const { return size==0; } template<typename T> T Stack_improve<T>::peek() const { return elements[size-1]; } template<typename T> void Stack_improve<T>::push(T value) { ensureCapacity(); elements[size++] = value; } template<typename T> T Stack_improve<T>::pop() { return elements[--size]; } template<typename T> int Stack_improve<T>::getSize() const { return size; } template<typename T> void Stack_improve<T>::ensureCapacity() { if(size>=capacity) { T* old = elements; capacity = 2 * size; // 改变capacity的大小 elements = new T[capacity]; // 将旧的数组的值复制过来 for(int i=0; i<size; i++) { elements[i] = old[i]; } delete [] old; } } #endif
main.cpp
#include <iostream> #include <string> #include <iomanip> #include <sstream> // 将数字转化为字符串 #include <algorithm> #include "E:\back_up\code\c_plus_code\chapter8\external_file\stack_improve.h" using namespace std; // 定义一个输出栈中元素的函数 template<typename T> void printStack(Stack_improve<T>& stack) { while(!stack.isEmpty()) { cout << stack.pop() << " "; } cout << endl; } int main(int argc, char *argv[]) { Stack_improve<int> int_stack; //声明模板类对象。必须指定类型 for(int i=0; i< 10; i++) { int_stack.push(i); } cout << "Peek int_stack " << int_stack.peek() << endl; printStack(int_stack); Stack_improve<string> string_stack; string_stack.push("Nanhudadao"); string_stack.push("Helloworld"); string_stack.push("stongarm"); printStack(string_stack); return 0; }
c++向量类:
C++提供了通用类Vector.用于存储列表对象 #include<vector>
vector类中的常用函数:
vector<type> Vname()
vector<type> Vname(size)
push_back(element) // 追加append()
pop_back() // 删除最后后一个元素
size()
at(index)
empty()
clear()
swap() // 交换两个向量中的内容
#include <iostream> #include <vector> #include <string> using namespace std; int main(int argc, char *argv[]) { // 使用atoi将字符串转化为int vector<int> intvect; for(int i=0; i<10; i++) { intvect.push_back(i*i); } cout << "Numbers in the vector: " << endl; for(int i=0; i<intvect.size(); i++) { cout << intvect[i] << " "; } cout << endl; intvect.pop_back(); // 不返回值 for(int i=0; i<intvect.size(); i++) { cout << intvect[i] << " "; } cout << endl; vector<string> stringvect; stringvect.push_back("hello"); stringvect.push_back("Nananan"); stringvect.push_back("Crypto"); cout << stringvect.empty() << endl; return 0; }
随机选取四种扑克牌:(将数组替换成向量)
#include <iostream> #include <iomanip> #include <cstdlib> #include <ctime> // 随机数 ctime #include <string> #include <vector> using namespace std; const int number_of_cards = 52; string suits[4] = {"Spades", "Hearts", "Diamond", "Clubs"}; string ranks[] = {"Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"}; int main(int argc, char *argv[]) { vector<int> deck(number_of_cards); // initialize for(int i=0; i<number_of_cards; i++) { deck[i] = i; } srand(time(0)); // shuffle for(int i=0; i<number_of_cards; i++) { int index = rand() % number_of_cards; int temp = deck[i]; deck[i] = deck[index]; deck[index] = temp; } // select four cards for(int i=0; i<4; i++) { cout << ranks[deck[i]%13] << " of " << suits[deck[i]/13] << endl; } return 0; }
二维数组也可以用向量表示:
#include <iostream> #include <iomanip> #include <cstdlib> #include <ctime> #include <string> #include <vector> using namespace std; void printMatrix(vector<vector<int > >& matrix) { for(int i=0; i<matrix.size(); i++) { for(int j=0; j<matrix[i].size(); j++) { cout << matrix[i][j] << " "; } cout << endl; } } int main(int argc, char *argv[]) { vector<vector<int> > matrix(4); // vector<vector<int> >必须有空格 for(int i=0; i<matrix.size(); i++) { matrix[i] = vector<int>(3); // 二维向量初始化 } for(int i=0; i<matrix.size(); i++) { for(int j=0; j<matrix[i].size(); j++) { matrix[i][j] = i+j; } } printMatrix(matrix); return 0; }
用栈实现表达式计算:
如: 51+(54*93+2))=?
#include <iostream> #include <string> #include <iomanip> #include <sstream> // 将数字转化为字符串 #include <algorithm> #include "E:\back_up\code\c_plus_code\chapter8\external_file\course.h" #include "E:\back_up\code\c_plus_code\chapter8\external_file\stack.h" #include "E:\back_up\code\c_plus_code\chapter8\external_file\stack_improve.h" #include <vector> #include <cctype> // include isdigit() using namespace std; // define function split() for split expression into number, operarot, return a vector vector<string> split(const string& expression); // calculate the expression int evaluateExpression(const string& expression); // return the result // process the stack void processAnOperator(Stack_improve<int> operandStack, Stack_improve<char> operator_stack); // 定义一个输出栈中元素的函数 template<typename T> void printStack(Stack_improve<T>& stack) { while(!stack.isEmpty()) { cout << stack.pop() << " "; } cout << endl; } int main(int argc, char *argv[]) { string expre; cout << "Enter the expression: " << endl; getline(cin, expre); cout << "Expression " << expre << " = " << evaluateExpression(expre) << endl; return 0; } // define function split() for split expression into number, operarot vector<string> split(const string& expression) { vector<string> v; string numberString; for(unsigned i=0; i<expression.length(); i++) { if(isdigit(expression[i])) // 开始的时候碰到数字 包含是多位数的可能 numberString.append(1, expression[i]); // 将数字追加在numberString后 else { if(numberString.size()>0) { v.push_back(numberString); // 考虑是多位数 numberString.erase(); // 清空erase } if(!isspace(expression[i])) // 不是数字,则一定是运算符或者括号 { string s; s.append(1, expression[i]); v.push_back(s); } } } //结束的时候碰到数字 if(numberString.size()>0) v.push_back(numberString); return v; } // define evualation function int evaluateExpression(const string& expression) { Stack_improve<int> operand_stack; // 存储操作数 Stack_improve<char> operator_stack; // 存储运算符 vector<string> token = split(expression); // phase1 scan the token 遍历分割后的表达式 for(int i=0; i<token.size(); i++) { if(token[i][0]=='+' || token[i][0]=='-') // +-运算符 { while(!operator_stack.isEmpty()&&(operator_stack.peek()=='+' || operator_stack.peek()=='-' || operator_stack.peek()=='*' || operator_stack.peek()=='/')) { processAnOperator(operand_stack, operator_stack); } operator_stack.push(token[i][0]); } else if(token[i][0]=='*' || token[i][0]=='/') { // process * / while(!operator_stack.isEmpty() && (operator_stack.peek()=='*' || operator_stack.peek()=='/')) { processAnOperator(operand_stack, operator_stack); } operand_stack.push(token[i][0]); } else if(token[i][0]=='(') { operator_stack.push(token[i][0]); } else if(token[i][0]==')') { while(operator_stack.peek()!='(') { processAnOperator(operand_stack, operator_stack); } operator_stack.pop(); // pop '(' out of the stack } else { operand_stack.push(atoi(token[i].c_str())); } } while(!operator_stack.isEmpty()) { processAnOperator(operand_stack, operator_stack); } return operand_stack.pop(); } // process operator: take a operator from the operatorStack and // apply it on the operands in the operands void processAnOperator(Stack_improve<int> operandStack, Stack_improve<char> operatorStack) { char op = operatorStack.pop(); int op1 = operandStack.pop(); int op2 = operandStack.pop(); if(op=='+') operandStack.push(op1+op2); else if(op=='-') operandStack.push(op1-op2); else if(op=='*') operandStack.push(op1*op2); else operandStack.push(op1/op2); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)