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);
}