C++(基础知识&c++11)

代码

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

  1. 把左值变成右值
  2. T &&t 原来是什么值就是什么值(&&不代表就是右值,也可以是左值,根据具体传值)

emplace_back

减少内存拷贝和移动,性能更好

push_back已弃用,直接用emplace_back

unordered_map

unordered_map使用hash做存储结构,key不排序,查询操作更快

map使用红黑树作为存储结构,key排序,插入操作更快

11特性总结

  • 智能指针
  • 右值引用
  • 匿名函数
  • STL容器
  • 正则表达式 

注意事项

  1. 智能指针用shared_ptr,有特殊需求再用unique_ptr
  2. weak_ptr是一种不控制对象生命周期的智能指针
  3. shared_ptr的线程安全问题分不同的场景,即引用计数是线程安全的,不同线程有独立的智能指针就是安全的,不同的线程去写操作同一个智能指针就不是安全的
  4. 右值引用&&不一定是右值引用,一个&肯定是左值,调用move肯定会变成右值,调用forward,传的&左值就是左值,传的右值就是右值,传个int 左值通过forward就变成右值
  5. 匿名lambda外部传值时通过捕获列表来传
  6. 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

线程池会用到

线程池

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值