代码
https://gitee.com/flying-guy/c--practice/tree/master/c++11
函数
函数是一组一起执行一个任务的语句。每个C程序都至少有一个函数,即主函数main(),所有简单的程序都可以定义其他额外的函数。
函数的声明
return_type function_name(parameter list)
{
body of the function
}
c++以函数为基础,java以类为基础。
java的接口只是定义类的规范,不会编译成具体的对象。
c++的接口通过.h文件引入,.h文件也不会参与编译,真正编译的是头文件实现的c文件。
.h文件解决程序的耦合问题。
指针函数
指针函数是指带指针的函数,即本质是一个函数。函数返回类型是某一类型的指针。
如:类型标识符 *函数名(参数表)
int *f(x,y);
传入指针类型,也要输出指针类型。
void* vParam 传入进来必须是指针,不管是什么类型(int、boolean、对象...),void代表无符号类型(相当与java中的Object)
函数指针
函数指针是指向函数的指针变量,即本质是一个指针变量。
如:
int (*f) (int x); //声明一个函数指针
func; //将func函数的首地址赋给指针
函数指针与指针函数最大的区别是函数指针是一个变量,与int类型一样是一个变量,只不过函数指针的这个变量的类型是函数。
内存
硬件角度
内存是计算机中必不可少的一个组成部分,是于CPU沟通的桥梁,计算机中所有的程序都是运行在内存中的。如:内存条。计算机中所有的程序都是运行在内存中的。
逻辑角度
内存是一块具备随机访问能力,支持读、写操作,用来存放程序及程序运行中产生的数据的区域。
内存单位
类型
位:(bit)是电子计算机中最小的数据单位。每一位的状态只能是0或1。
字节:1Byte = 8bit,是内存基本的计量单位。
KB:1KB = 1024Byte。也就是1024个字节。
MB:1MB = 1024KB。类似的还有GB、TB。
内存编制
计算机中的内存按字节编址,每个地址的存储单位可以存放一个字节(8个bit)的数据,CPU通过内存地址获取指令和数据,并不关心这个地址所代表的空间具体在什么位置、怎样分布,因为硬件的设计保证一个地址对应着一个固定的空间,所以说:内存地址和地址指向的空间共同构成了一个内存单元。
内存地址
内存地址通常用十六进制的数据表示,指向内存中某一块区域。
内存地址分配规则
连续的,一个挨着一个。
当对象需要申请内存时,先给这个对象分配一个编码,这个编码就是内存地址。
指针指向的内存区域能存储不同的类型。
基本数据类型
int(4字节)
short(2字节)
long(4字节)
char(1字节)
double(8字节)
long double(10字节)
引用类型
long和int在早期16位电脑时候int2字节,long4字节。计算机发展到现在,一般32、64下,long和int一样。而java的long是8字节。
内存组成

C语言内存组成
BSS段存放着全局成员变量,这个成员变量在编译的时候无法确定它的大小,也无法确定它的内存地址。类似于对象的成员变量分配在BSS,代码段将函数转化成汇编语言,汇编语言全部由指令组成,将其全部存放在代码段中。

安卓内存组成
app运行时,方法是在java栈,对象被实例化是是放在堆区。
安卓内存分配基于安卓虚拟机,虚拟机是由C/C++开发的。
C语言是直接转化成汇编语言,通过CPU来执行。
指针
指针数组
定义:int *p[n];
数组里面的每一个元素都是指针。
指针优先级
定义:() > [] > *
数组指针(也称为行指针)
定义:int (*p)[n]
优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨向n个整型数据的长度。
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p = a; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
指针数组与数组指针的区别
指针数组:代表的是一个数组,每个元素代表的是存放一个地址。
数组指针:代表的是一个变量,这个变量存放着指针。
结构体
相当于java中的javaBean,可以将一些变量封装成对象。
通过struct声明一个结构体(相当于java中的class)
//Student相当于类名
//student和a可以不定义,表示结构变量,也就Student类型的变量
struct Student //声明一个结构体
{
char name[50]; //当前结构体的声明
int age;
}student,a; //声明一些成员变量
//使用typedef定义一个别名
typedef struct{
char name[50];
int age;
}Student;
结构对齐
定义:对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,它就被称作自然对齐。比如在32位CPU下,假设一个整型变量的地址为0x00000004,那它就是自然对齐。
结构体大小
定义:当结构体需要内存过大,使用动态内存申请。结构体占用字节数和结构体内字段有关,指针占用内存就是4/8字节,因此传指针比传值效率更高。
结构体存储原则
内存对齐。
存储变量时地址要求对齐,编译器在编译程序时会遵循两条原则:
- 结构体变量中的成员的偏移量必须是成员大小的整数倍(0被认为是任何数)。
- 结构体大小必须是所有成员大小的整数倍,也即所有成员大小的公倍数。

对齐前需要访问i需要访问两个地址,对齐后只需要访问一个地址。
共用体
定义:共用体是一种特殊的数据类型,允许你在相同的内存位置存储不同的数据类型。你可以定义一个带有多个成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
与结构体相似,更省空间。
union Data
{
int i;
float f;
char str[20];
}data;
注意:共用体占用的内存应足够存储共用体中的最大的成员。例如,在上面的实例中,Data将占用20给字节的内存空间,因为在各个成员中,字符串所占用的空间是最大的。下面的实例将显示上面的共用体占用的总内存大小。
库
定义:在windows平台和linux平台下都大量存在库。Android中也存在库。库是指一个容器文件,里面装着函数,由于windows和linux的平台不同(主要是编译器、汇编器和连接器的不同),因此二者库的二进制不兼容。
库的种类
| 动态库 | 静态库 | |
| windows | .dll | .lib |
| android | .so | .a |
编译一个动态库

编译一个库的流程
使用gc++编译库


相关命令
-fPIC:压制警告
-shared:编译动态库
-static:编译静态库
动态库与静态库的区别

动态库:运行时把so的文件拿出来加载到内存,给用到的方法分配内存。
静态库:在编译过程中,会把.a打包到目标文件中,然后再加载进内存,运行时不会再去.a中拿文件函数。
因此,使用动态库时,目标文件速度慢,文件体积小。使用静态库时,目标文件速度快,文件体积大。
类型转换
强转类型
C风格的强制类型转换不管什么类型的转换统统是
TYPE b = (TYPE)a
强制转换类型
const_type:强转一些const修饰的属性。(c++中的const=java中的final)
static_cast:静态类型转换,发生在编译时,强转静态多态。如int转char。
dynamic_cast:动态类型转换,发生在运行时,强转动态多态。如子类和父类之间的多态类型转换。
reinterpret_cast:重新解释类型,但没有进行二进制的转换、
四种类型转换的格式
//static_cast(TYPE)(a) = 四种转换类型之一(需要转换的类型)(需要转换的对象)
TYPE B = static_cast(TYPE)(a)




转换操作符

异常处理


文件与流




容器
序列式容器
元素排列顺序与元素本身无关,由添加顺序决定
关联式容器
C++11
编译运行
g++ -o 1-1 main.cpp -std=c++11

优先使用make_shared来构造指针
auto sp1 = make_shared<int>(100); //优先使用make_shared来构造智能指针
//相当于
shared_ptr<int> sp2(new int(100));
后面的智能指针先析构

通过shared_from_this()返回this指针,不要将this指针作为shared_ptr返回出来,因为this指针本质上是一个裸指针,因此,这样可能会导致重复析构。可以使用 public std::enable_shared_from_this<A>
多一个智能指针赋值的时候引用计数就会+1
unique_ptr
独占的智能指针
想独占一个对象,又想让它自动释放
unique_ptr<int> my_ptr(new int); //正确
unique_ptr<int> my_ptr2 = my_ptr; //错误,独占性锁不能赋值给其他
unique_ptr<int> my_other_ptr = std::move(my_ptr);//正确 my_ptr资源被移到my_other_ptr
weak_ptr
shared_ptr智能指针还有内存泄漏的情况,当两个对象相互使用一个shared_ptr成员变量指向双方,会造成循环引用,使引用计数失效,从而内存泄漏。
- 可以使用weak_ptr解决shared_ptr循环引用时的死锁问题。
- weak_ptr是一种不控制对象生命周期的智能指针,它的构造和析构不会引起计数器的增加和减少。

- weak_ptr不能直接操作对象的成员、方法。
- 在使用wp前需要调用wp.expired()函数判断一下weak_ptr是否已过期
shared_ptr是不是线程安全的?
1、安全,如果每个线程的sp是独立的,引用计数是安全的,多线程下,赋值几次,值就加几次
2、不安全,不同线程如果共用一个sp就不是安全的
每个智能指针在自己的作用域起作用
右值引用
优化性能,减少深拷贝

&&不代表就是右值,也可以左值
&肯定是左值
move
拿别人的资源,把别人的资源置空
将左值变成右值
左值能赋值
右值是临时的,不能赋值
forward
- 把左值变成右值
- T &&t 原来是什么值就是什么值(&&不代表就是右值,也可以是左值,根据具体传值)

emplace_back
减少内存拷贝和移动,性能更好
push_back已弃用,直接用emplace_back
unordered_map
unordered_map使用hash做存储结构,key不排序,查询操作更快
map使用红黑树作为存储结构,key排序,插入操作更快
11特性总结
- 智能指针
- 右值引用
- 匿名函数
- STL容器
- 正则表达式
注意事项
- 智能指针用shared_ptr,有特殊需求再用unique_ptr
- weak_ptr是一种不控制对象生命周期的智能指针
- shared_ptr的线程安全问题分不同的场景,即引用计数是线程安全的,不同线程有独立的智能指针就是安全的,不同的线程去写操作同一个智能指针就不是安全的
- 右值引用&&不一定是右值引用,一个&肯定是左值,调用move肯定会变成右值,调用forward,传的&左值就是左值,传的右值就是右值,传个int 左值通过forward就变成右值
- 匿名lambda外部传值时通过捕获列表来传
- STL容器无序容器和有序容器的实现,容器操作尽量用新接口,会有更快的一个性能
线程
主要成员函数
get_id()
获取线程ID,返回类型std::thread::id对象。
joinable()
判断线程是否可以加入等待。
join()
等该线程执行完成后才返回。
detach()
将本线程从调用线程中分离出来,允许本线程独立执行。但是当主线程结束的时候,即便是detach()出去的子线程不管有没有完成都会被强制杀死。
互斥量
mutex
std::mutex,独占的互斥量,不能递归使用
std::lock_guard:构造函数加锁,析构函数释放锁
std::unique_lock:可以手动去lock和unlock


原子变量
std::atomi<int> foo = 0; //错误初始化
std::atomi<int> foo(0); //正确初始化
异步操作
std::future
std::aysnc
起一个线程异步去执行

std::promise
std::promise和std::future配合,两个线程数据交换时可以用promise
std::packaged_task
线程池会用到
线程池

26万+

被折叠的 条评论
为什么被折叠?



