C++提高编程
本阶段主要针对C++泛型编程和STL技术做详细讲解,探讨C++更深层的使用
1、模板
1.1 模板的概念
模板就是建立通用的模具,大大提高复用性
模板的特点:
- 模板不可以直接使用,它只是一个框架
- 模板的通用并不是万能的
1.2 函数模板
- C++另一种编程思想称为泛型编程,主要利用的技术就是模板
- C++提供两种模板机制:函数模板和类模板
1.2.1 函数模板语法
函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型来代表。
语法:
template<typename T>
函数声明或定义 // 定义函数模板前面都要加上上面那一行
解释:
template -- 声明创建模板
typename -- 表明其后面的符号是一种数据类型,可以用class代替
T -- 通用的数据类型,名称可以替换,通常为大写字母
#include<iostream>
#include<string>
using namespace std;
// 定义一个交换的函数模板
template<typename T> // 声明一个模板,告诉编译器后面代码中紧跟着的T不要报错, T是一个通用数据类型
void swap1(T& a, T& b) {
T tmp;
tmp = b;
b = a;
a = tmp;
}
void test() {
int a = 10;
int b = 20;
// 两种方式使用函数模板
// 1、自动类型推导 不填类型,让其自己推导,但是不能使用不同的类型
//swap1(a, b);
// 2、显示指定类型 用< >指定具体的数据类型
swap1<int>(a, b);
cout << a << "\t" << b << endl;
}
int main() {
test();
system("pause");
return 0;
}
template <class T, class M>
void print(T a, M b) {
cout << a <<"\t" << b << endl;
}
int a = 10;
int b = 10;
char c = 'c';
print(a, c);
print<int>(a, c); // 指定第一个,即T类型
print<int, int>(a, c);
1.2.2函数模板注意事项
注意事项:
- 自动类型推导,必须推导出一致的数据类型才可以使用
- 模板必须要确定出T的数据类型, 才可以使用
1、
int a = 10;
int b = 20;
char c= "c";
// 1、自动类型推导 下面这种事错误的,因为无法确定是int还是char
//swap1(a, c);
2、模板必须要确定出T的数据类型, 才可以使用,就是有的函数可能就没有传输可以让其识别T数据类型的参数,无法确定其数据类型,这种就不能用自动类型推导,而是直接给确定一种数据类型
template <typename T>
void func(){
cout<<"func调用,无法通过自动类型推导确定T的数据类型";
}
void test(){
//func() //错误,无法调用,因为模板无法确定T的数据类型
//调用方法,第二种方式
func<int>() // 指定一种数据类型
}
1.2.3函数模板案例
案例描述:
- 利用函数模板封装一个排序函数,可以对不同类型数组进行排序
- 排序规则从大到小,排序算法为选择排序
- 分别用char数组和int数组进行测试
#include<iostream>
#include<string>
using namespace std;
// 定义一个交换的函数模板
template<typename T> // 声明一个模板,告诉编译器后面代码中紧跟着的T不要报错, T是一个通用数据类型
void swap1(T& a, T& b) {
T tmp;
tmp = b;
b = a;
a = tmp;
}
template <class T>
void selectSort(T Arr[], int len) {
for (int i = 0; i < len-1; i++) {
int local = i;
for (int j = i; j < len; j++) {
if (Arr[j] > Arr[local]) {
local = j;
};
}
swap1(Arr[i], Arr[local]);
}
}
// 打印数组的模板
template <class T>
void print_list(T Arr[], int len) {
for (int i = 0; i < len; i++) {
cout << Arr[i];
}
cout << endl;
}
void test1() {
int int_arr[] = { 9,7,8,5,4,2,3,1,0 };
char char_arr[] = "abcgdwert";
//int len = sizeof(Arr) / sizeof(Arr[0]);
selectSort(int_arr, sizeof(int_arr) / sizeof(int_arr[0]));
selectSort(char_arr, sizeof(char_arr) / sizeof(char_arr[0]));
print_list(int_arr, sizeof(int_arr) / sizeof(int_arr[0]));
print_list(char_arr, sizeof(char_arr) / sizeof(char_arr[0]));
}
int main() {
test1();
system("pause");
return 0;
}
1.2.4普通函数与函数模板的区别
- 普通函数调用时可以可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显式指定的方式,可以发生隐式类型转换
int add(int a, int b) {
return a + b;
}
template <class T>
T add_T(T a, T b) {
return a + b;
}
void test2() {
int a = 10;
int b = 10;
char c = 'c';
cout<<add(a, c)<<endl; // 发生隐式转换,将c转换为整型ascll码
//add_T(a, c); // 报错,自动类型转换,不支持隐式类型转换
add_T<int>(a, c); // 显式支持
}
总结:建议使用显式指定类型的方式,调用函数模板,因为可以自己确定通用类型T
1.2.5普通函数与函数模板的调用规则
如下:(重载的情况下)
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表<>来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
//普通函数
void myPrint(int a, int b) {
cout << "调用普通函数" << endl;
}
template<class T>
void myPrint(T a, T b) {
cout <<"调用模板函数" << endl;
}
template<class T>
void myPrint(T a, T b, T c) {
cout << "调用重载模板函数" << endl;
}
void test2() {
int a = 10;
int b = 20;
// 1、优先调用普通函数
myPrint(a, b);
// 2、强制调用模板
myPrint<>(a, b);
// 3、模板重载
myPrint(a, b, 10);
// 4、函数模板可以产生更好的匹配,优先调用函数模板
char c = 'c';
char d = 'd';
myPrint(c, d);
/*调用普通函数
调用模板函数
调用重载模板函数
调用模板函数*/
}
1.2.6模板的局限性
局限性:
- 模板的通用性并不是万能的
template <class T>
void func(T a, T b) {
a = b;
}
在上述代码中提供的赋值操作,如果传入的是a和b是一个数组,就无法实现了
再如
template <class T>
void func(T a, T b){
if (a>b){
.....
}
}
在上述代码中,如果T的数据类型传入的是自定的数据类型,如类,结构体,也无法正常运行
因此C++为了解决这样的问题,提供了模板的重载,可以为这些特定的数据类型,提供具体化的模板
#include<iostream>
#include<string>
using namespace std;
#include<string>
class Person {
public:
string m_name;
int m_age;
};
template<class T>
bool myCompare(T &a, T &b) {
if (a == b) { return true; }
else { return false; }
}
// 利用具体化Person的版本实现代码,具体化优先调用 把上面的bool myCompare(T &a, T &b)
// 复制过来,把T具体化, 然后前面加上 template<> 说明这是一个模板的重载,
template<> bool myCompare(Person& p1, Person& p2) {
if (p1.m_name == p2.m_name && p1.m_age == p2.m_age) { return true; }
else { return false; }
}
void test2() {
Person p1 = { "xiaoming", 18 };
Person p2 = { "xiaoming", 18 };
cout << myCompare<Person>(p1, p2) << endl;
}
int main() {
test2();
system("pause");
return 0;
}
1.3 类模板
1.3.1类模板语法
类模板作用:
- 建立一个通用类,类中的成员 数据类型可以不具体指定,用一个虚拟的类型来代表
**语法: **
template<class T>
类
解释:
template: 声明创建模板
typename : 表明其后面符号是一种数据类型,可用class代替
T : 通用数据类型,名称可以替换,通常为大写字母24
template <class NameType, class AgeType>
class Person {
public:
Person(NameType name, AgeType age) {
this->m_name = name;
this->m_age = age;
}
void showPerson() {
cout << "姓名" << m_name << "\t" << "年龄:" << m_age << endl;
}
NameType m_name;
AgeType m_age;
};
void test3() {
Person<string, int>p1("孙悟空", 2000); //类模板必须有参数列表,也就是说不能使用自动推导数据类型
p1.showPerson();
}
总结:类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板
1.3.2类模板与函数模板区别
类模板与函数模板区别主要有两点:
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
template <class NameType = string, class AgeType=int>
class Person{....}
....
Person p("猪八戒", 2000); // 不用参数列表指定,使用默认参数类型
1.3.3类模板中成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机是有区别的:
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
// 普通类
class Person1 {
public:
void showPerson1() {
cout << "person 1" << endl;
}
};
class Person2 {
public:
void showPerson2() {
cout << "person 2" << endl;
}
};
template <class T>
class MyClass {
public:
T obj;
void func1() { obj.showPerson1(); }
void func2() { obj.showPerson2(); }
};
void test3() {
MyClass<Person1>m;
m.func1();
m.func2(); // 在不调用的时候,不报错,调用的时候才报错,说明函数调用时候才会创建函数
}
总结:类模板中的成员函数兵部使一开始就创建的,在调用时才去创建
1.3.4类模板对象做函数对象
学习目标:
- 类模板实例化出的对象,像函数传参的方式
一共有三种传入方式
- 指定传入的类型 : 直接显示对象的数据类型
- 参数模板化 : 将对象中的参数变为模板进行传递
- 整个类模板化 : 将这个对象类型 模板化进行传递
#include<iostream>
#include<string>
using namespace std;
#include<string>
template <class NameType, class AgeType=int>
class Person {
public:
Person(NameType name, AgeType age) {
this->m_name = name;
this->m_age = age;
}
void showPerson() {
cout << "姓名:" << m_name << "\t" << "年龄:" << m_age << endl;
}
NameType m_name;
AgeType m_age;
};
// 第一种 传入方式 指定传入类型
void func1(Person<string>& p) {
p.showPerson();
}
// 第二种 传入方式 参数模板化
template<typename T>
void func2(Person<T>& p) {
cout<<T的数据类型<<typeid(T).name()<<endl;
p.showPerson();
}
// 第三种 整个类模板化
template <class T>
void func3(T& p) {
p.showPerson();
}
void test3() {
Person<string>p("孙悟空", 5000);
func1(p);
func2(p);
func3(p);
}
int main() {
test3();
system("pause");
return 0;
}
总结:
- 通过类模板创建的对象,可以有三种方式向函数中进行传参
- 使用比较广泛的时第一种:指定传入的类型
1.3.5类模板与继承
当类模板碰到继承,需要注意以下几点
- 当子类继承的父类是一个类模板的时候,子类在声明的时候,要指定出父类中T的类型
- 如果不指定,编译器无法给子类分配内存
- 如果向灵活指定出父类中T的类型,子类也需要变为类模板
#include<iostream>
#include<string>
using namespace std;
#include<string>
template<class T>
class Base {
public:
T m;
};
//继承方式一 指定父类中T的数据类型
class Son :public Base<int> {
public:
Son(int m) {
this->m = m;
cout << this->m << endl;
}
};
// 二、将子类也变为类模板,T2 指向父类中的T
template <class T1, class T2>
class Son2 :public Base<T2> {
public:
Son2(T1 n, T2 m) {
this->m = m;
this->n = n;
cout << this->m << "\t" << this->n << endl;
}
T1 n;
};
void test4() {
Son s1(10);
Son2 <int, int>s2(10, 20);
}
int main() {
test4();
system("pause");
return 0;
}
总结:如果父类是类模板,子类需要指定出父类中的T的数据类型
1.3.6类模板成员函数类外实现
目标:找我类模板中成员函数的类外实现
- 需要在类外实现 的函数名上一行加模板的声明 说明是一个模板
- 在函数名前加作用域
- 作用域后加参数列表,说明是一个模板类
#include<iostream>
#include<string>
using namespace std;
#include<string>
template<class T1, class T2>
class Base {
public:
Base(T1 name, T2 age);
void show();
T1 m_name;
T2 m_age;
};
// 构造函数类外实现
template<class T1, class T2>
Base<T1, T2> ::Base(T1 name, T2 age) {
this->m_name = name;
this->m_age = age;
}
template <class T1, class T2>
void Base<T1, T2>::show() {
cout << "姓名:" << this->m_name << endl;
cout << "年龄:" << this->m_age << endl;
}
void test4() {
Base<string, int> b("chen", 18);
b.show();
}
int main() {
test4();
system("pause");
return 0;
}
总结: 类模板中成员函数类外实现,需要加上模板参数列表
1.3.7类模板分文件编写
目标:掌握类模板成员函数分文件编写产生的问题及解决方式
问题:
- 类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
- 解决方式1:直接包含cpp源文件, cpp源文件中 会包含.h 文件
- 解决方式2:将声明和实现写到同一个文件中,并更改后缀名.hpp,hpp是约定俗成的名称,不是强制
解决方式1:
#include"base.cpp"
解决方式2:
// hpp文件
#pragma once
#include<iostream>
#include<string>
using namespace std;
#include<string>
template<class T1, class T2>
class Base {
public:
Base(T1 name, T2 age);
void show();
T1 m_name;
T2 m_age;
};
template<class T1, class T2>
Base<T1, T2> ::Base(T1 name, T2 age) {
this->m_name = name;
this->m_age = age;
}
template <class T1, class T2>
void Base<T1, T2>::show() {
cout << "姓名:" << this->m_name << endl;
cout << "年龄:" << this->m_age << endl;
}
// 主文件
#include"Base.hpp"
void test4() {
Base<string, int> b("chen", 18);
b.show();
}
总结:主流的解决方式是第二种,将类模板成员函数写到一起,并将后缀名改为.hpp
1.3.8类模板与友元
目标:掌握类模板配合友元函数的类和类外实现
- 全局函数类内实现,直接在类内声明友元即可
- 全局函数类外实现,需要提前让编译器知道全局函数的存在
#include<iostream>
#include<string>
using namespace std;
#include<string>
// showPerson2中需要有Person类所以要声明
template <class NameType, class AgeType = int>
class Person;
// 类外实现 所以要让编译器知道这个函数的存在,所以要放在前面
template <class NameType, class AgeType = int>
void showPerson2(Person<NameType, AgeType>p) {
cout << "姓名:" << p.m_name << "\t" << "年龄:" << p.m_age << endl;
}
template <class NameType, class AgeType = int>
class Person {
// 1、全局函数类内实现
friend void showPerson(Person<NameType, AgeType>p) {
cout << "姓名:" << p.m_name << "\t" << "年龄:" << p.m_age << endl;
}
// 2、全局函数 类外实现
// 加空模板参数列表 表明是一个函数模板
// 如果全局函数 是类外实现,需要让编译器提前知道这个函数的存在
friend void showPerson2<>(Person<NameType, AgeType>p);
public:
Person(NameType name, AgeType age) {
this->m_name = name;
this->m_age = age;
}
private:
NameType m_name;
AgeType m_age;
};
void test5() {
Person <string, int> p("孙悟空", 5000);
showPerson(p);
}
int main() {
test5();
system("pause");
return 0;
}
总结:建议全局函数做类内实现,用法简单,而且编译器可以直接识别
1.3.9类模板案例
案例描述:实现一个通用的数组类,要求如下
- 可以对内置数据类型以及自定义数据类型的数据进行存储
- 将数组中的数据存储到堆区
- 构造函数中可以传入数组的容量
- 提供对应的拷贝构造函数以及operator= 防止浅拷贝问题
- 提供尾插法和尾删法对数组中的数据进行增加和删除
- 可以通过下标的方式访问数组中的元素
- 可以获取数组中当前元素的个数和数组的数量
MyArr.hpp
#pragma once
#include<iostream>
using namespace std;
#include<string>
template<class T>
class MyArr {
public:
// 构造函数
MyArr(int capacity) {
cout << "有参构造函数" << endl;
this->m_capacity = capacity;
this->m_size = 0;
this->pAddress = new T[this->m_capacity];
}
// 拷贝构造
MyArr(const MyArr& arr) {
cout << "拷贝构造函数" << endl;
this->m_capacity = arr.m_capacity;
this->m_size = arr.m_size;
// 深拷贝
this->pAddress = new T[arr.m_capacity];
for (int i = 0; i < m_size; i++) {
this->pAddress[i] = arr.pAddress[i];
}
}
// operator= 运算符重载 防止浅拷贝问题
MyArr& operator=(MyArr& arr) {
cout << "operator=重载" << endl;
// 先判断原来堆区是否有数据, 如果有先释放
if (this->pAddress != NULL) {
delete[] this->pAddress;
this->pAddress = NULL;
this->m_capacity = 0;
this->m_size = 0;
}
this->m_capacity = arr.m_capacity;
this->m_size = arr.m_size;
this->pAddress = new T[arr.m_capacity];
for (int i = 0; i < arr.m_size; i++) {
this->pAddress[i] = arr.pAddress[i];
}
return *this;
}
// 尾插法对数组中数据进行增加 const修饰的常量指针 支持非左值输入 指向的值不发生变化
void append(const T& val) {
if (this->m_capacity == this->m_size) {
cout << "数组内存已满" << endl;
return;
}
else {
this->pAddress[this->m_size] = val;
this->m_size++;
}
}
// 尾删法对数组中数据进行删除
void pop() {
// 让用户访问不到最后一个元素,即是尾删
if (this->m_size == 0) {
return;
}
this->m_size--;
}
// 通过下标访问数组中元素
T& operator[](int index){
return this->pAddress[index];
}
// 返回数组容量
int getCapacity() {
return this->m_capacity;
}
// 返回数组容量
int get_size() {
return this->m_size;
}
~MyArr()
{
cout << "析构函数" << endl;
if (this->pAddress != NULL) {
delete[] this->pAddress;
this->pAddress = NULL;
}
}
private:
T* pAddress;
int m_capacity;
int m_size;
};
主函数
#include"MyArr.hpp"
void test6() {
MyArr<int>arr1(10);
MyArr<int>arr2(arr1);
MyArr<int>arr3(20);
arr1.append(1);
arr1.append(2);
arr1.append(3);
arr1.append(4);
arr3 = arr1;
cout << arr3[3] << endl;
arr3[2] = 10;
cout << arr3[2] << endl;
cout << arr3.getCapacity() << endl;
cout << arr1.get_size() << endl;
}
int main() {
test6();
system("pause");
return 0;
}
2、STL初识
2.1 STL诞生
2.2 STL基本概念
- STL(standard Template Library 标准模板库)
- STL从广义上分为:容器container 算法algorithm 和迭代器 iterator
- 容器和算法之间通过迭代器进行无缝连接
- STL几乎所有的代码都采用了模板类或者模板函数
2.3 STL六大组件
STL大体分为六大组件, 分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
- 容器:各种数据结构,如vector、list、deque、set、、map等,用来存储数据
- 算法:各种常用的算法 如sort、find、copy、for_each等
- 迭代器:扮演了容器与算法之间的胶合剂
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
- 空间配置器:负责空间的配置与管理
2.4 STL中容器、算法、迭代器
容器:存放各种数据
STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组、链表、数、栈、队列、集合、映射表等
这些容器分为序列式容器和关联式容器两种:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
算法:解决问题的方法
有限的步骤,解决逻辑或者数学上的问题 algorithms
算法分为质变算法和非质变算法
质变算法:是指运算过程中会更改区间内的元素的内容,如拷贝、替换、删除等
非质变算法:是值运算过程中国不会更改区间内的元素内容,如查找、计数、遍历、寻找极值等
迭代器:容器和算法之间的粘合剂
提供一种方法,是指能够依次寻访,某个容器所含的各个元素,而又无须暴露该容器的内部表示方式
每个容器都有自己专属的迭代器
迭代器的使用非常类似于指针,初学阶段我们可以先理解迭代器为指针
迭代器种类
种类 | 功能 | 支持运算 |
---|---|---|
输入迭代器 | 对数据的只读访问 | 只读,支持++、++、!+ |
输出迭代器 | 对数据只写访问 | 只写 支持 ++ |
前向迭代器 | 读写操作,并能向前推进迭代器· | 读写 |
双向迭代器 | 读写操作,并能向前和向后操作 | 读写 |
随机访问迭代器 | 读写操作,可以以跳跃式的方式访问任意数据,功能最强的迭代器 | 读写支持++、--、[n]、-n、<、<=、>=、> |
常用的容器中迭代器种类为双向迭代器和随机访问迭代器
2.5容器算法迭代器初识
STL中最常用的容器为Vector,可以理解为数组
2.5.1vector存放内置数据类型
容器:vector
就是一个模板类,创建即实例化的时候要加参数列表
算法:for_each(起始位置,终止位置,执行的函数(不加括号))
需要头文件algorithm
迭代器:vector<int>::iterator
指向第一个元素位置 .begin() 最后一个元素的下一个位置 .end()
#include<iostream>
#include<string>
#include<vector>
using namespace std;
#include<algorithm> // 标准算法头文件
template <class T>
void myPrint(T val) {
cout << val << endl;
}
void test() {
// 创建一个vector容器, 数组 ,容器中数据类型为int
vector<int>v;
// 尾插法添加数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
//通过迭代器访问容器中的数据
vector<int>::iterator itBegin = v.begin(); // 起始迭代器 指向容器中的第一个元素
vector<int>::iterator itEnd = v.end(); // 结束迭代器 指向容器中最后一个元素的下一个位置
//// 第一种遍历方式
//while (itBegin != itEnd) {
// cout << *itBegin << endl;
// itBegin++;
//}
// 第二种遍历方式
/*for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}*/
// 第三种遍历方式
for_each(v.begin(), v.end(), myPrint<int>); // 利用标准STL提供的遍历算法
}
int main() {
test();
system("pause");
return 0;
}
2.5.2Vector存放自定义数据类型
vector中存放自定义数据类型, 并打印输出
#include<iostream>
#include<string>
#include<vector>
using namespace std;
#include<algorithm> // 标准算法头文件
// 定义自定数据类型 类
class Person {
public:
Person(string name, int age) {
this->mName = name;
this->mAge = age;
}
string mName;
int mAge;
};
void test() {
vector<Person> v;
Person p1("孙悟空", 399999);
Person p2("sunwukong", 399999);
Person p3("zhubajie", 3434341);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
// 遍历打印
vector<Person>::iterator it_begin = v.begin();
vector<Person>::iterator it_end = v.end();
/*while (it_begin != it_end) {
cout << it_begin->mName << " " << it_begin->mAge << endl;
cout << (*it_begin).mName << " " << (*it_begin).mAge << endl;
it_begin++;
}*/
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
cout << it->mName << " " << it->mAge << endl;
}
}
void test2() {
//存放自定义数据类型 指针
vector<Person*> v;
Person p1("孙悟空", 399999);
Person p2("sunwukong", 399999);
Person p3("zhubajie", 3434341);
v.push_back(&p1);
v.push_back(&p2);
v.push_back(&p3);
for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) {
cout << (**it).mName << " " << (**it).mAge << endl;
cout << (*it)->mName << " " << (*it)->mAge << endl;
}
}
int main() {
//test();
test2();
system("pause");
return 0;
}
2.5.3vector 容器嵌套容器
vector中嵌套容器, 将所有的数据进行遍历输出
#include<iostream>
#include<string>
using namespace std;
#include<vector>
void test() {
// 嵌套一个vector int类型的容器
vector<vector<int>> v;
vector<int> v1;
vector<int> v2;
vector<int> v3;
// 添加数据
for (int i = 0; i < 3; i++) {
v1.push_back(i + 1);
v2.push_back(i + 2);
v3.push_back(i + 3);
}
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
// 遍历
for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) {
// it指向嵌套的容器的地址,需要*取出,然后再用其迭代器,取出里面的值
for (vector<int>::iterator vit = it->begin(); vit != (*it).end(); vit++) {
cout << *vit << endl;
}
}
}
int main() {
test();
system("pause");
return 0;
}
标签:函数,int,提高,C++,template,include,void,模板
From: https://www.cnblogs.com/fuxingming/p/16750734.html