文章目录
模板类
用C++在编写规模稍大些的程序时,应该把类的声明和实现分开,这样可以提高编译效率。只要cpp文件没有改动,编译时编译器不会对该文件进行重新编译,也就是说代码已经被固化起来。
模板类的就是根据变量类型的不同产生对应类型的基类代码。因为变量类型的不同,本质上的代码也就不同,所以模板类的代码是动态的。模板类需要在使用到的地方利用声明模板的typename或者class参数的时候,才会即时生成代码。那么当我把模板声明和实现分开的时候,这个即时过程因为编译器只能找到声明而找不到实现,所以在调用模板类的地方就会报错。
所以在编写模板类的时候要将声明和实现统一写到头文件中。
虚函数
C++中为了实现多态引入了virtual关键字,用该关键字声明的函数在调用时是动态链接而非静态链接。virtual可以声明虚函数和纯虚函数,虚函数的声明方式为:virtual 类型名 函数名();,纯虚函数的声明方式为:virtual 类型名 函数名() = 0;。两者的最大不同之处在于:虚函数可以在声明的类中实现;纯虚函数不可以在声明的类中实现,而且继承该类的派生类必须给出该纯虚函数的实现方法。
无法被实例化的类被称为虚类。含有虚函数的类不一定是虚类,含有纯虚函数的类一定是虚类。使用C++的纯虚函数构造虚类可以实现Java中接口的相关功能。
析构函数
派生类会继承基类的构造函数和析构函数,在实例化派生类时会先调用基类的构造函数再调用派生类的构造函数,而在销毁实例时会先调用派生类的析构函数再调用基类的析构函数。
当一个基类指针指向一个派生类实例时,若基类析构函数声明为虚函数,delete基类指针,会先调用派生类的析构函数,再调用基类的析构函数。若基类析构函数没有声明为虚函数,delete基类指针,只会调用基类的析构函数,而不会调用派生类的析构函数,这样会造成销毁实例的不完全。
所以一个基类或派生类没有虚析构函数编译器会发出警告。
优先队列
优先队列不是一个容器类,它需要借助vector类或者queue类来实现。优先队列的定义方法为:priority_queue<变量类型,存储容器=vector<对应变量类型>,比较类=less<对应变量类型> >,存储容器缺省时默认使用vector,比较类缺省时默认使用less<对应变量类型>建立最大堆,可以传入greater<对应变量类型>类建立最小堆,也可以建立一个新类并在类中对()进行重载,然后将该类传入。主要函数有以下几个:
empty();如果队列为空,则返回真
pop();删除对顶元素,删除第一个元素
push(value);加入一个元素
size();返回优先队列中拥有的元素个数
top();返回优先队列对顶元素,返回优先队列中有最高优先级的元素
堆
C++STL实现堆的方法是提供了堆操作的函数,这些函数作用在vector或queue实例或其它线性结构上来实现堆。这些函数被声明在algorithm头文件中。
make_heap(begin,end,cmp=less);建堆函数
push_heap(begin,end,cmp=less);压堆函数,将线性结构中最后一个元素当作需要压入的数据,所以若使用了vector做线性容器,需要先调用push_back()。
pop_heap(begin,end,cmp=less);弹堆函数,将堆顶元素弹出并放到线性结构的最后一个位置,所以若使用了vector做线性容器,需要紧接着调用pop_back()完成弹堆。
sort_heap(begin,end,cmp=less);堆排序函数
四个函数的参数类型及意义相同,begin表示线性结构的头部指针或迭代器起始位置,end表示线性结构的尾部或迭代器结束位置,cmp是比较函数指针,该参数可缺省,缺省时默认调用less<对应变量类型>()函数建立最大堆,可以传入greater<对应变量类型>()函数建立最小堆,也可自行编写比较函数传入,也可利用Lambda表达式编写匿名函数传入。注意调用less<对应变量类型>()函数或greater<对应变量类型>()函数时一定要加上括号,less<对应变量类型>是一个类,less<对应变量类型>()是less<对应变量类型>类中重载()函数。
new
三种用法
plain new
此方法在C++中的声明如下:
void *operator new(std::size_t)throw(std::bad_alloc);
此方法为通常情况下的使用方法,在分配失败的情况下会抛出std::bad_alloc异常,而不会返回NULL。
nothrow new
此方法在C++中的声明如下:
void *operator new(std::size_t, const std::nothrow_t &)throw();
顾名思义,此方法为不抛出异常的方法,在分配失败的情况下会返回NULL,而不会抛出异常。
使用方法举例:
int *a = new(nothrow) int;///nothrow new
delete a;
int *b = new(nothrow) int[32];///nothrow new
delete[] b;
placement new
此方法在C++中的声明如下:
void *operator new(std::size_t, void *);
此方法为在一块已经拥有的内存空间上来构造一个对象,在一个数组上构造或者在一块已分配的堆空间上等等。
使用方法举例:
class test{
int n;
public:
test(int n):n(n){}
~test(){}
}
int a[32];
int *b = new(a) test;///placement new
b->test::~test;///显式调用析构函数销毁对象
此方法没有内存分配失败异常,因为该方法根本没有分配内存,只是在一块已有的内存空间上调用了构造函数。正是由于上述原因,使用此方法分配的内存空间也无需delete,如若使用了delete则可能会将原有的内存空间释放掉。当需要销毁对象时,无法使用delete销毁,应当显式调用析构函数销毁对象。
初始化
&esmp; 内置型变量new后跟()会分配空间并将空间内初始化为0,不跟()则仅分配空间不进行初始化。举例如下:
int *a = new int[32];///仅分配32个int的内存空间
int *b = new int[32]();///分配32个int的内存空间并将其全部初始化为0
类型变量new后跟不跟()都会调用无参构造函数进行初始化。举例如下:
string *a = new string[32];///分配空间并为每个对象调用无参构造函数
string *b = new string[32]();///效果与上条语句相同
string *c = new string[32]("test");///编译器报错
map
insert
方法一:
map<string, int> a;
a.insert(pair<string, int>("test", 0));
方法二:
map<string, int> a;
a["test"] = 0;
find
探测某元素是否存在:
map<string, int> a;
if(a.find("test") != a.end()){
cout << "Exist" << endl;
}
获取某元素的迭代器指针:
map<string, int> a;
itr = a.find("test");
二分查找
binary_search
查找某个元素是否出现
vector<int> a = {4, 3, 2, 1};
if(binary_search(a.begin(), a.end(), 3, greater<int>())==true){ /// 非升序排列需使用greater
cout << "Exist" << endl;
}
lower_bound
查找第一个等于或大于某元素的位置
vector<int> a = {4, 3, 2, 1};
int i = lower_bound(a.begin(), a.end(), 3, greater<int>()) - a.begin(); /// 非升序排列需使用greater
cout << i << endl;
upper_bound
查找第一个大于某元素的位置
vector<int> a = {4, 3, 2, 1};
int i = upper_bound(a.begin(), a.end(), 3, greater<int>()) - a.begin(); /// 非升序排列需使用greater
cout << i << endl;