第17章 模板与类型转换
- 模板的定义及目的
将一种数据类型定义为参数,然后将不同数据类型按照实参形式传送而实现代码重用(类似与java的泛型机制),减少由于类型不同而产生的无所谓的重载。 - 模板的定义及使用
#include<iostream>
using std::cout;
using std::endl;
template<class T>
T min(T x,T y){
return (x<y)?x:y;
}
int main(){
int n1 = 2,n2 = 10;
double d1 = 1.5,d2 = 5.6;
//隐式调用
cout<<"较小的整数"<<min(n1,n2)<<endl;
//显示调用
cout<<"较小的实数"<<min<double>(d1,d2)<<endl;
return 0;
}
- 模版的特殊化,模板中有一个具体实现
#include<iostream>
using namespace std;
template<class T>
class Pair{
T value1,value2;
public :
Pair(T first,T second){
value1 = first;
value2 = second;
}
T module(){
return 0;
}
};
template<>
class Pair<int>{
int value1,value2;
public:
Pair(int first,int second){
value1 = first;
value2 = second;
}
int module(){
return value1%value2;
}
};
int main(){
Pair<int> myints(100,75);
Pair<float>myfloats(100.0,75.0);
cout<<myints.module()<<endl;
cout<<myfloats.module()<<endl;
return 0;
}
重载和函数模版
函数模板重载的参数匹配规则如下:
1.寻找和使用最符合函数名和参数类型的涵数。
2.寻找一个函数模版,将其实例化产生一个匹配的函数。
3.寻找可以通过类型转换的参数匹配的重载函数。
4.如果未找到则调用错误,如果找到的多于一个匹配则出现二义性。运行时类型识别
在C++中类型识别是指只有一个指向基类的指针或引用时,确定一个对象的准确类型。派生类的特殊性可以通过定义虚函数加以体现,运行时类型检查只有在必要时才使用(影响效率).两种使用方法:1.运行时识别类型typeid
shape *sh1 = new circle;
cout<<typeid(*sh1).name()<<endl;
2.运行时类型强制转换dynamic_cast
#include<iostream>
#include<typeinfo>
using namespace std;
class shape{
int s1;
int s2;
int s3;
public:
virtual void draw();
};
void shape::draw(){
cout<<"shape drawing "<<endl;
}
class circle:public shape{
int t1;
int t2;
int t3;
public :
void draw();
};
void circle::draw(){
cout<<"circle drawing"<<endl;
}
int main(){
shape *sh1 = new circle;
cout<<typeid(*sh1).name()<<endl;
sh1->draw();
circle *cp = dynamic_cast<circle *>(sh1);
if(cp){
cout<<"case sucessful"<<endl;
cp->draw();
}
return 0;
}
强制类型转换运算符
1.static_cast
最常用的类型转换符,在正常状况下的类型转换,如把int转换为float,如:
int i;float f; f=(float)i;
或者f=static_cast<float>(i);
2.const_cast
用于取出const属性,把const类型的指针变为非const类型的指针,如:
const int *fun(int x,int y){} int *ptr=const_cast<int *>(fun(2.3))
3.dynamic_cast
该操作符用于运行时检查该转换是否类型安全,但只在多态类型时合法,即该类至少具有一个虚拟方法。dynamic_cast与static_cast具有相同的基本语法,dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。如:
class C
{
//…C没有虚拟函数
};
class T{
//…
}
int main()
{
dynamic_cast<T*> (new C);//错误
}
此时如改为以下则是合法的:
class C
{
public:
virtual void m() {};// C现在是 多态
}
4.reinterpret_cast
interpret是解释的意思,reinterpret即为重新解释,此标识符的意思即为数据的二进制形式重新解释,但是不改变其值。如:
int i; char *ptr="hello freind!"; i=reinterpret_cast<int>(ptr);这个转换方式很少使用。
第18章 容器和迭代器
- 迭代器的概念及意义
迭代器是一种检查容器内的元素并遍历元素的数据类型。 常用的迭代器
1.输入迭代器
必须能读取其所指向的值,支持的操作有*p,++p,p++,p!=q,p==q,凡是支持这五类操作的类都可以称为输入迭代器,*p只允许读取,不许修改。2.输出迭代器
必须能对指向的序列进行写操作,支持的操作同上五种
但是*p所返回的值允许修改,而不一定要读取。3.前向迭代器
支持的操作相同,但*p既可以读取也可以修改。4.双向迭代器
进一步支持操作符自减如p–,–p.5.随机存取迭代器
允许随机访问序列的任意值,如指针。迭代器类型可以使用解引用操作符(*)来访问,如*iter = 0;
- C++中的容器有顺序容器和关联容器
顺序容器
将单一容器聚集起来成为容器,然后根据位置来存储和访问数据 1.向量vector类似与java中的arraylist
主要操作有:
v.push_back();//在数组的最后一个添加一个值
v.size();//数组大小
v.empty();//判空
v[n];//返回v中位置为n的元素
v1=v2;把v1的元素替换成v2的副本
v1==v2;判断是否相等
vector进行初始化时如果没有指定元素初始化,则标准库自行提供一个初始化值进行初始化。2.双端队列
元素可以从队列的两端出队和入队,支持使用[]访问,必须引入头文件deque
主要操作有:
d.push_back(3);//从后插入
d.push_front(10);//从前插入
d.insert(1,88);//在一号位置插入,原来1号及后面的数据往后移,
d.begin();//起始位置
deque不提供容量操作,capacity(),reverse();3.列表
列表主要用于存放双向链表,可以从任意一端开始遍历,列表还提供了拼接操作,将一个序列中的元素插入到另一个序列中,需要引入头文件list
主要操作有:
l.push_back();
l.push_front();
l.erase();//删除
#include<iostream>
#include<list>
using namespace std;
int main(){
//定义list
list<int>elemets;
//定义迭代器
list<int>::iterator iter;
//向list中当前的指针位置插入,直到最后一位
elemets.push_back(8);
elemets.push_back(5);
//在当前的位置插入,直到最前一位
elemets.push_front(2);
//进行迭代遍历
for(iter = elemets.begin();iter != elemets.end();iter++){
cout<<"元素:"<<*iter<<endl;
}
cout<<"删除元素首位后"<<endl;
//删除list首位
elemets.erase(elemets.begin());
//进行迭代遍历
for(iter = elemets.begin();iter!=elemets.end();iter++){
cout<<"元素:"<<*iter<<endl;
}
return 0;
}
- 关联容器
通过键值存取数据的集合。关联容器的读取和存储和数据写入的顺序无关,只根据键值来指定对应的元素。
- 1.集合和多集
一个集合是一个容器,所包含的元素是唯一的(set)。集合的元素按一定的顺序排列(平衡二叉树),并被作为集合中的实例。通过链表实现。如果要修改集或多级容器中的元素值,必须先删除原有的元素,在插入新元素。
集合和多集的区别:
集(set)支持唯一键,集中的值都是特定的,并且只出现一次。而多集(multiset)中可以出现副本键,一个值可以出现多次。
#include<iostream>
#include<set>
using namespace std;
int main(){
set<int>set1;
for(int i=0;i<10;i++){
set1.insert(i);
}
for(set<int>::iterator p=set1.begin();p!=set1.end();++p){
cout<<*p<<"";
}
cout<<endl;
if(set1.insert(3).second){
cout<<"插入成功"<<endl;
}else{
cout<<"插入失败"<<endl;
}
int a[] = {4,1,1,1,1,0,5,1,0};
multiset<int> A;
A.insert(set1.begin(),set1.end());
A.insert(a,a+9);
cout<<endl;
for(multiset<int>::iterator p =A.begin();p!=A.end();++p){
cout<<*p<<"";
}
cout<<endl;
return 0;
}
- 2.映射和多重映射
基于某一个类型Key的键集的存在,提供对T类型数据的高效检索。映射(map)不支持副本键,多重映射(multimap)支持。键的本身是不能被修改的,除非删除。
集合和映射的区别:集合的键和值是key类型的,而map键和值是一个pair结构中的两个分量.
#include<iostream>
#include<map>
using namespace std;
int main(){
// 键 值 排序方式
map<char,int,less<char> >map1;
map<char,int,less<char> >::iterator mapIter;
map1['c']=3;
map1['d']=4;
map1['b']=2;
map1['a']=1;
for(mapIter=map1.begin();mapIter!=map1.end();++mapIter){
cout<<""<<(*mapIter).first<<":"<<(*mapIter).second;
}
map<char,int,less<char> >::const_iterator ptr;
ptr=map1.find('d');
cout<<endl<<""<<(*ptr).first<<"键对应于值:"<<(*ptr).second;
map<int,int,less<int> >map2;
map2[1]=2;
map2[3]=4;
map<int,int,less<int> >::const_iterator ptr2;
ptr2 = map2.find(1);
cout<<(*ptr2).first<<"键对应值:"<<(*ptr).second;
return 0;
}
容器适配器
- 1.栈
常用操作:
push(x);
pop();
top();
empty();
size(); - 2.队列
常用操作:
…
front();
back();
… - 3.优先级队列
底层使用vector或deque实现,当添加元素时,按照优先顺序插入。默认情况下元素的比较是通过比较函数对象less<<>>执行.