c++提高编程(上)
本章主要讨论c++泛型编程 和 stl 技术
————————————————————
文章目录
产出:
1.模板
1.1.模板
c++除了面向对象思想,还有种编程思想,泛型编程。
而泛型编程主要是依靠模板来实现。
模板,建立通用的模具,大大提高复用性。
但也有其不能及,例如,模板不能直接使用,本质只是框架。
模板并不是通用的
1自动类型推导
2显示指定类型
//函数模板的注意事项
#include<iostream>
using namespace std;
template<class T>
void myChange(T &a,T &b){
T temp = a;
a = b;
b = temp;
};
//myFun01()错误,因为没有确认S的数据类型
//myFun01<int>()正确
template<class S>
void myFun01(){
};
void intChange(int &a,int &b){
int temp = a;
a = b;
b = temp;
};
void doubleChange(double &a,double &b){
double temp = a;
a = b;
b = temp;
};
int main(){
int a = 10;
int b = 20;
double c = 30.5;
double d = 40.6;
//intChange(a,b);
myChange(a,b);//自动类型推导
//注意事项,1.模板函数体中必须使用到 通用数据类型T,否则报错。
//即 必须确定 T 的数据类型。才能使用
// 必须推导出一致的数据类型才可,例如传入参数 不是int,则报错
myChange<int>(a,b);//显示指定类型
cout<<a<<endl;
cout<<b<<endl;
system("pause");
return 0;
}
——————————————————————
1.2、函数模板案例
(将不同数据类型的数组由大到小排序)
//将两个测试函数test01,test02模块化的时候,运行结果不对。
//分析应该是sizeof(T)写法不对,对的话结果肯定是对的,希望有老板看到帮解决下哈哈哈我就下搁置了
错的地方备注了
#include<iostream>
using namespace std;
template<class T>
void change(T&a,T&b ){
T temp = a;
a = b;
b = temp;
};
template<class T>
void mySort(T Array[],int len){
for(int i=0;i <len;i++){
int Max = i;//定义最大数下标
for(int j=i+1;j<len;j++){
if (Array[Max] < Array[j]){
Max = j;//更新最大数下标
};
};
if(Max != i){
change(Array[Max],Array[i]);
};
};
};
template<class T>
void outArray(T array[],int len){
for(int i=0;i<len;i++){
cout<<array[i]<<" ";
};
cout<<endl;
};
void test01(){
char array01[] = "acegik";
int len = sizeof(array01)/sizeof(char);
outArray(array01,len);
mySort(array01,len);
outArray(array01,len);
};
void test02(){
int array02[] = {
1,3,5,7,9};
int len = sizeof(array02)/sizeof(int);
outArray(array02,len);
mySort(array02,len);
outArray(array02,len);
};
//
//template<class T>
//void test(T array01[]){
// int len01 = 6;
// outArray(array01,len01);
// mySort(array01,len01);
// outArray(array01,len01);
//};
int main(){
/*char array01[] = "acegik";
int array02[] = {1,3,5,7,9};*/
test01();
test02();
/*test(array01);
test(array02);*/
system("pause");
return 0;
}
1.3、普通函数与函数模板区别
普通函数可以直接发生 自动类型转换(隐式类型转换)
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
如果利用显示指定类型,可以发生隐式类型转换
所以建议使用显示指定类型的方式调用函数模板 直接自己确定好。
1.4、普通函数与函数模板调用规则
1.如果普通函数和函数模板同时存在,优先调用普通模板
2.可以通过空模板参数列表来强制调用函数模板
也可以在添加参数
3.函数模板可以发生重载
4.如果函数模板可以产生更好的匹配,优先调用函数模板。
#include<iostream>
using namespace std;
void change(int a,int b){
cout<<"普通函数"<<endl;
};
template<class T>
void change(T a,T b){
cout<<"函数模板"<<endl;;
};
void test01(){
int a = 10;
int b = 20;
change<int>(a,b);
}
int main(){
test01();
system("pause");
return 0;
}
1.5、模板局限性
模板并不是万能的,特定数据类型,需要用具体化方式做特殊实现。
//模板局限性 例如下面案例 比大小,自定义的数据类型就不可以。需要重载模板版本
#include<iostream>
using namespace std;
#include<string>
class Person{
public:
Person(string name,int age){
this->name = name;
this->age = age;
};
string name;
int age;
};
template<class T>
bool IsSame(T a,T b){
if(a == b){
return true;
}else{
return false;
}
};
void test01(){
int a = 10;
int b = 10;
bool end = IsSame(a,b);
if (end==true){
cout<<"相同"<<endl;
}else{
cout<<"不相同"<<endl;
}
};
//提供模板重载版本,原模板无法判断自定义数据类型
template<>bool IsSame(Person a,Person b){
if(a.name == b.name){
return true;
}else{
return false;
};
};
void test02(){
Person p1("毕羽",22);
Person p2("毕羽",22);
bool end = IsSame(p1,p2);
if (end==true){
cout<<"相同"<<endl;
}else{
cout<<"不相同"<<endl;
}
}
int main(){
test02();
system("pause");
return 0;
}
1.6、类模板
#include<iostream>
using namespace std;
#include<string>
template<class A,class B = int>
class Person{
public:
Person(A name,B age){
this->name = name;
this->age = age;
};
void show(){
cout<<this->name<<":"<<this->age<<endl;
};
string name;
int age;
};
int main(){
//类模板使用的时候只能使用 显示指定类型,不可以使用自动类型推导
Person<string,int>p1("毕羽",22);
p1.show();
//类模板中的模板参数列表,可以指定默认类型
Person<string>p2("陈曦",20);
p2.show();
system("pause");
return 0;
}
类模板中的成员函数 并不是一开始就创建的,而是被调用时被创建
//类模板中的成员函数 并不是一开始就创建的,而是被调用时被创建
#include<iostream>
using namespace std;
#include<string>
class Person01{
public:
void show01(){
cout<<"showPerson01"<<endl;
}
string name;
int age;
};
class Person02{
public:
void show02(){
cout<<"showPerson02"<<endl;
}
string name;
int age;
};
template<class T>
class Myclass{
public:
T obj;
void fun01(){
obj.show01();
};
void fun02(){
obj.show02();
};
};
void test01(){
Myclass<Person01>m1;
m1.fun01();
Myclass<Person02>m2;
m2.fun02();
};
int main(){
test01();
system("pause");
return 0;
};
1.7、类模板实例化的对象 向函数中传参
三种方式
1.指定传入类型(最为常用,阿牛我喜欢用这个)
2.参数模板化
3.整个类模板化
#include<iostream>
using namespace std;
#include<string>
template<class T1,class T2>
class Person{
public:
Person(T1 name,T2 age){
this->name = name;
this->age = age;
}
T1 name;
T2 age;
void show(){
cout<<this->name<<"-"<<this->age<<endl;
};
};
//指定传入类型
//void fun00(Person<string,int>&p1){
// p1.show();
//};
//参数模板化
//template<class T1,class T2>
//void fun00(Person<T1,T2>p1){
// p1.show();
//};
//整个类模板化
template<class T>
void fun00(T p1){
p1.show();
cout<<typeid(T).name()<<endl;
};
int main(){
Person<string,int>p1("阿牛",20);
fun00(p1);
system("pause");
return 0;
}
1.8、类模板与继承
两点,1 、父类如果是类模板,子类需要指明父类模板参数才可以实例化对象
2、类模板的子类如果想灵活些,很简单,那就变成类模板
#include<iostream>
using namespace std;
#include<string>
template<class T1>
class Person{
public:
T1 obj;
};
//指明父类的模板参数类型才可以实例化对象
class Son1 : public Person<int>{
};
//或者也变成类模板
template<class T1,class T2>
class Son2 : public Person<T2>{
public:
Son2(){
cout<<typeid(T1).name()<<endl;
cout<<typeid(T2).name()<<endl;
}
T1 obj;
};
void test01(){
Son2<string,int>s2;
};
int main(){
test01();
system("pause");
return 0;
}
1.9、类模板的类外实现
#include<iostream>
using namespace std;
#include<string>
template<class T1>
class Person{
public:
Person(T1 age);/*{
this->age = age;
};*/
void fun01();/*{
cout<<"___"<<endl;
};*/
int age;
};
template<class T1>
Person<T1>::Person(T1 age){
this->age = age;
};
template<class T1>
void Person<T1>::fun01(){
cout<<"___"<<endl;
};
void test01(){
Person<int> p1(10);
p1.fun01();
};
int main(){
test01();
system("pause");
return 0;
}
1.10、类模板份文件编写
————————————————
2、STL
2.1、SLT基本概念1
之前学过的面向对象思想(封装,继承,多态)
封装:例如类,把事物的相同属性和行为抽象出来。
继承:派生类继承基类的属性和行为
多态:父类指针指向子类对象,实现多种形态。
其目的 主要还是 提升代码复用性
之前的泛型编程思想(模板),其目的也是复用性的提升
但是相同的问题,可以采用不同的算法和数据结构区解决。人与人之间不会一直统一,这种程度上 增加了代码的重复性(因为达到的目的都是一样的)。
为了建立数据结构和算法的一套标准,诞生了STL。
SLT广义上 分为
容器:各种数据结构:vector,deque,list,set,map
算法:常用算法,sort,for_each,copy,find.
迭代器:目前阶段可以理解为指针,是容器和算法的粘合剂,写好的算法需要通过迭代器来控制容器。
2.2、STL基本概念2
standard template librairy
容器(container),
算法(algorithm),
迭代器(iterator),
仿函数,行为类似函数,可作为算法的某种策略
适配器,用于容器,迭代器,仿函数 这些接口的修饰
空间配置器:用于空间的配置和管理
STL容器
就是 将 运用最广泛的数据结构实现出来。长荣的数据结构:数组,链表,栈,树,集合,映射,队列等。
这些容器 分为 序列式容器和关系式容器
序列式容器:值的排序,容器中每个元素均有固定的位置。
关系式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系。
————————————————
算法
有限的步骤,解决逻辑或数学上的问题。
质变算法和非质变算法
质变算法:运算过程中会改变区间中的元素,例如拷贝,添加,删除等
非质变算法反之,例如查找,计数,遍历,寻找极值等。
—————————————————
迭代器:目前阶段可以理解为指针,是容器和算法的粘合剂,写好的算法需要通过迭代器来控制容器。
每个容器都有自己的专属迭代器
输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机访问迭代器。
(只读) (只写)
2.3 vector容器
vector容器的数据结构和数组非常相似,也称为单端数组
不同之处是数组是静态的,一经定义就不可以拓展,而vector可以动态扩展
其动态扩展并不是在原有空间后扩展,因为无法确定这之后的内存空间是否被使用。所以另找一块更大的内存空间,将原数据拷贝到新空间,释放原k空间
———————————— push_back()
|
|
|
| front(); back()
————————————pop_back();
v.rend() v.begin() v.rbegin() v.end() 迭代器
头之前 头 尾 尾之后
insert()
2.3.1、vector容器 三种遍历
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
void fun01(int QQ){
cout<<QQ<<endl;
};
int main(){
vector<int>v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
//while遍历
vector<int>::iterator v1_s = v1.begin();
vector<int>::iterator v2_e = v1.end();
while(v1_s != v2_e){
cout<<*v1_s;
v1_s++;
};
cout<<endl;
//for循环遍历
for(vector<int>::iterator Xi = v1.begin();Xi != v1.end();Xi++){
cout<<*Xi;
};
cout<<endl;
/*STL提供的遍历*/
for_each(v1.begin(),v1.end(),fun01);
system("pause");
return 0;
}
2.3.2、vector容器存放自定义数据类型
#include<iostream>
using namespace std;
#include<string>
#include<vector>
class Person{
public:
Person(string name,int age){
this->name = name;
this->age = age;
};
string name;
int age;
};
void test01(){
vector<Person>v;
Person p1("aaa",01);
Person p2("bbb",02);
Person p3("ccc",03);
Person p4("ddd",04);
Person p5("eee",05);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
for(vector<Person>::iterator v2 = v.begin();v2 != v.end();v2++){
cout<<(*v2).name<<endl;
cout<<v2->age<<endl;
};
cout<<"---------------"<<endl;
};
void test02(){
vector<Person*>v5;
Person p1("aaa",01);
Person p2("bbb",02);
Person p3("ccc",03);
Person p4("ddd",04);
Person p5("eee",05);
v5.push_back(&p1);
v5.push_back(&p2);
v5.push_back(&p3);
v5.push_back(&p4);
v5.push_back(&p5);
for(vector<Person*>::iterator v6 = v5.begin();v6!=v5.end();v6++){
cout<<(*v6)->name<<endl;
cout<<(*v6)->age<<endl;
};
};
int main(){
test01();
test02();
system("pause");
return 0;
};
2.3.3、vector容器嵌套
俄罗斯套娃,大vector套5个小vector。
for循环中,<&g