1 补充知识点
c与c++区别
c语言和C++不能说一个面向过程,一个面向对象:
- 面向过程和面向对象只是一种编程思想,不是针对某一种语言的。Linux内核中的C语言就是运用面向对象的思想编写的。
- 只是说,在实现面向对象编程时,C++提供的语法实现起来比C语言更方便;仅此而已。
g++编码选项
-std=c++11
表示使用c++11的标准来编译。因为C++11基本上是行业标准-ansi
关闭gnu的特性,使代码全面符合ansi要求,从而具有高移植性。避免包含一些只有gnu独有的语法
c/c++ extern关键字
声明函数或变量。函数可以省略,但变量省略就成定义了,而不是声明。声明不会分配内存,而定义会占用内存空间。
c++ const
关键字与c语言差别:
- C++ 对 const 的特性做了调整,C++ 规定,全局 const 变量的作用域仍然是当前文件,在其他文件中是不可见的,这和添加了static关键字的效果类似
- 使用的是 GCC,那么可以通过添加 extern 关键字来增大 C++ 全局 const 变量的可见范围,其他文件使用 extern 声明后就可以使用了。
c++ new 和 delete
C++ 中的 new 和 delete 分别用来分配和释放内存,它们与C语言中 malloc()、free() 最大的一个不同之处在于:
- 用 new 分配内存时会调用构造函数,用 delete 释放内存时会调用析构函数。构造函数和析构函数对于类来说是不可或缺的,所以在C++中我们非常鼓励使用 new 和 delete。
对指针的理解
- 基本类型变量仅仅是地址的注记符而已,这个变量就是数据的内存首地址,假如数据类型为int,则从首地址开始的连续四个内存地址,都用来存储这个int类型的数据。
- 编译时,会将变量名替换为首地址
- 而指针变量,它注记指代的内存块,存储的不是数据,而是另一片内存地址,而这另一片内存地址才是真实的数据。
- 所以:使用指针是间接获取数据,使用变量名是直接获取数据,前者比后者的代价要高
2 STL(standard Template Libray)
模板所支持的类型是宽泛的,没有限制的,我们可以使用任意类型来替换,这种编程方式称为泛型编程
STL是基于泛型编程思想,利用模板技术。包含了计算机科学领域常用的基本数据结构和基本算法
2.1 顺序容器
顺序容器(也称"序列式容器")将一组具有相同类型的元素****以严格的线性形式组织起来。主要分为三类: vector、deque、list
vector:
- 内部实现实际上是动态数组,存取任何元素都能在常数时间内完成,在尾端增删元素具有较佳的性能;头部插入要整体移动所有数据,效率低
- vector有多个构造函数,默认的构造函数是构造一个初始长度为0的内存空间,且分配的内存空间是以2的倍数动态增长的,在push_back的过程中,若发现分配的内存空间不足,则重新分配一段连续的内存空间,其大小是现在连续空间的2倍,在将原先空间中的元素复制到新的空间中,性能消耗较大。
- 随机访问效率高
deque:
- 类似vector的结构,随机访问效率高。
- 不同之处:vector还维护首地址,双端插入的效率都很高;vector按块存储,在扩展空间的时候新增一个块,不用复制,效率较高
list:
- 双向链表结构
- 随机访问效率低,都要遍历
- 高效的随机插入/删除操作;尤其在首尾 插入,效率很高,只需要改变元素的指针。
2.2 关联容器之set、multiset、map、multimap
关联容器内的元素是排序的,插入任何元素,都能按照相应的排序准则来确定位置,特点是在查找时具有非常好的性能,通常以平衡二叉树方式实现,插入和检索的时间都是O(logn)
set和multiset:
- set是一种关联性容器,底层使用红黑树实现,插入删除操作时仅仅移动指针即可,不涉及内存的移动和拷贝,所以效率比较高
- set中的元素都是唯一的,而且默认情况下会对元素进行升序排列
- set和multiset的区别是:set插入的元素不能相同,但是multiset可以相同
map和multimap:
- map容器提供一个键值对(key/value)容器,map与multimap差别仅仅在于multiple允许一个键对应多个值
- map内部自建一棵红黑树(一种自平衡二叉树),这棵树具有数据自动排序的功能,所以在map内部所有的数据都是有序的,以二叉树的形式进行组织
2.3 容器适配器adapter
适配器(Adaptors)是标准库中的一个通用概念,容器、迭代器和函数****都有适配器。本质上,一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样。一个容器适配器(Container adaptors)在一种已有的容器类型上面进行包装,使其行为看起来像一种不同的类型
标准库定义了三个序列容器适配器:
- stack: 栈,先进后出;底层使用deque
- queue:队列,先进先出;底层使用deque
- priority_queue: 优先级队列:优先级大的先出队,底层数据结构默认是大根堆。优先级队列底层默认把数据组成一个大根堆结构,而大根堆的构建就需要在一个内存连续的数组上(堆中结点和它左右孩子的关系是通过下标计算的),vector动态数组底层是绝对连续的,而deque是分段连续的,所以用vector
迭代器
迭代器用于遍历对象集合的元素,从实现角度来看,**迭代器是一种将***operator、operator->、operator++、operator–**等指针操作予以重载的class template
。
2.4 函数对象
如果一个类将()运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就是函数对象。函数对象是一个对象,但是使用的形式看起来像函数调用,实际上也执行了函数调用,因而得名。
3 C++11
C++11标准,就是新增的特性
1 auto关键字
auto关键字和static关键字区别:
1、static变量存放在静态存储区,在程序整个运行期间都不释放;而auto变量存放在动态存储区,随着生命周期的结束而立即释放。
2、static变量只赋值一次,以后就不用赋值;而auto变量在函数每调用一次都要赋初值。
3、如果用户不对static变量赋初值,则默认为0或’\0’;而auto变量为不确定值。
c++默认就是auto,所以auto关键字写和不写没区别。
C++11给auto关键字赋予了新的含义:
- 使用它来做自动类型推导。也就是说,使用了 auto 关键字以后,编译器会在编译期间自动推导出变量的类型,这样我们就不用手动指明变量的数据类型了
nullptr关键字
用nullptr表示空指针
更优雅的初始化方法
C++11之前,只有数组能使用初始化列表:int arr[3] = {1, 2, 3}
;
c++11开始,各种数据类型都可以用初始化列表来初始化:map(int, string){{1, "hello"}, {2, "hh"}}
;
基于范围的for循环
void foo() {
vector<int> v{1, 2, 3};
for (auto x:v) {
cout << x << endl;
}
}
编译断言:static_assert
编译期间断言:
// c++ 11 compile-time assert
static_assert ( bool_constexpr , string )
// bool_constexpr: 常量表达式(不能包含变量)string:
// 如果bool_constexpr表达式为false, 这个string就是编译时候报的错误
// run-time assert
assert(ptr != NULL)
lambda表达式
格式:
[capture list] (params list)mutable exception-> return type {
function body
}
除了捕获列表[capture list]
,其他项参数列表、mutable、异常、返回类型都可以省略
捕获列表的捕获方式和捕获范围:
function对象包装器
可以降不同的可调用对象,包装成一个类模板,从一个入口统一调用:functon<return type<typename, ...>>
bind
类似python的偏函数,将可调用对象的部分函数固定起来。
auto x = [](int a, int b) {
return a+b;
}
auto y = bind(x, 1, 2);
cout << y() << endl;