《Essential C++》这本书,是适合从C选手快速过度到C++选手的 一本书,下面是个人记录
第一章:基础语法
第一章主要就是C语言基础,这里类似于表达式 数组 条件语句 循环语句,就不多概述了。
:::info vector:可动态扩增的Array
Vector,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库。
vector,是一个能够存放任意类型的动态数组,能够增加和压缩数据。
:::
第二章:面向过程的编程风格
指针和引用的区别:
在本质上来说 指针和引用都是变量 存放的都是被引用对象的地址
指针变量本身可以被寻址
int a =100;
int *p= &a;//p存放的是a的地址
int **p1 = &p;//p1存放的就是p的地址
- 而引用变量地址却不可被寻址,假如引用变量为r,&r操作得到的只能是r所指向对象的地址,而不是r本身的地址。
- 数组元素允许是指针常量,而不能是引用例如 a作为一个引用数组 a[0]=1; 无法确定是a[0]的值为1还是a[0]所引用的值为1,容易产生二义性。
- 引用不能为空,而指针可以为空。你可以只声明一个指针变量,而不去给它赋值。一个未指向任何对象的指针,其地址值为0.
- 指针可以有多级,而引用只能一级
- "sizeof 引用"得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小
- 当我们对指针进行解引用操作时(*p),一定要确定其值并非0,对于引用来说,因为它一定会代表某个对象,所以不需要做这样的检查
堆内存
堆允许程序在运行时动态地申请某个大小的内存空间。
在C++中,通过new出来的对象,需要使用delete加以释放。
inline函数:用来替代C语言的宏定义
inline函数可以解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题
#include
//函数定义为inline即:内联函数
inline char* f(int i) {
return (i % 2 > 0) ? "奇" : "偶";
}
int main()
{
int i = 0;
for (i=1; i < 100; i++) {
printf("i:%d 奇偶性:%s /n", i, f(i));
}
}
/*
普通情况运行的时候,系统通过循环要一次次调用f函数的。
使用inline之后,每次运行相当于在把printf()里的f(i)调用直接换成了return (i % 2 > 0);这样就提高了运行效率
*/
:::info
也就是说,在函数体很小的情况下,使用Inline函数可以提高效率
需要注意的一点是,将函数定义为inline,仅仅是对编译器的一种建议,编译器是否执行这个请求,需视编译器而定。
⚠️⚠️⚠️
inline函数虽好,但是需要慎用
内联是以代码膨胀复制为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码, 将使程序的总代码量增大,消耗更多的内存空间。
如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
:::
第三章:泛型编程风格
STL部分
这部分和Java以及Kotlin里面的容器几乎差不多
顺序型容器 vector与list
vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。
查找效率很高,为 O(1)。但是插入和删除就比较麻烦。
而vector和java里的ArrayList一样,都具有自动扩容的功能。
不同的编译器实现的扩容方式不一样,VS2015中以1.5倍扩容,GCC以2倍扩容。
list底层由双向链表实现,因此内存空间是不连续的。所以插入时间复杂度低,查找时间复杂度高。
关联容器 map和set
java,里面也有类似的数据结构,map一般表示一对对的key/value组合,俗称键值对。而set就只含有key。
set不允许键值重复,可以用此解决一些算法问题,例如环形链表问题。
map也不允许键值重复,但是map允许修改键对应的值。set则不能修改键值。
set和map底层都是红黑树,红黑树是一种具有自动平衡功能的二叉树,在set中,如果想要修改键值,那么就会破坏红黑树的结构, 所以STL中将set的迭代器设置成const,不允许修改迭代器的值。
queue和stack这个和Java里面的双向链表很像,就不多说了。
Iterator(迭代器)
迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。
vector
vector
for (i = v.begin(); i != v.end();