复合类型:
引用:
- 基本概念: 绑定对象,不发生拷贝(当普通赋值时,为确保创建新的对象,分配新的地址,对象拷贝操作),相当于对象的别名
- 初始化:type & name = obj;
- 只能绑定在对象(左值)上,之后所有操作都在绑定的对象进行
- 因为一旦绑定了对象,就不能更改,因此定义时必须初始化
- 引用非对象:没有 地址 / 内存(数据),因此不能定义引用的引用,也没有const 引用
- 初始化时对象类型必须要与引用的类型一致:
- 因为类型不一致时,编译器为确保引用绑定相同类型,创建了临时量,我们想要对绑定的对象操作,但目前绑定的只是临时量,不会影响对象本身,所以无法做到我们期望的结果,因此这种行为定义为非法
- 赋值:name = val;对引用绑定的对象赋值
指针:
- 基本概念:指向对象的地址
- 初始化: type * name = &obj取地址;
- 定义(分配内存)时不需要初始化(填充数据)
- 指针是对象:有自己的内存空间,因此可以定义指针的指针 / 指向指针的引用:type *& name = obj;
- 初始化时对象类型必须要与指针的类型一致
- 赋值:
- 可以改变指针指向的对象(p = &obj || **p = &p2(指向指针的指针) || p = p2(指向同一对象,p2指针作为右值取p2的值)),也可以改变所指向对象的值( *p解引用 = Val)
特殊指针:
- 空指针:没有指向任何对象,=nullptr / = 0 / =null(0)(尽量避免使用),试图访问指针指向对象的值,行为不被允许
- 无效指针:指向无效内存,试图访问指针指向对象的值,引发错误
- void*指针:可以指向任何类型对象, 限制:无法操作所指对象,
技巧:
- 如何判断对象类型?从右往左读,右侧优先级更高
- 如何判断我们改变了,指针指向的对象 还是 对改变所指向对象的值?赋值永远改变左侧的对象
- 同符号有多重含义,如何区分?在定义声明时组成复合类型,在表达式则为取地址/解引用
const常量对象:(非常量->常量 , 常量 !->非常量)
- 基本概念:限制变量的值无法改变(防止意外修改)
- 初始化:
- 定义时必须初始化:const type name = ......
- 允许非常量初始化常量
- 不允许对常量赋值
- 作用域:文件
对const的引用
- 与普通引用的区别:
- 初始化时,类型不一定要一致,只要可以转换为引用的类型就可以(非const, 字面值,表达式)
- 不允许通过引用,改变绑定的对象的值,但并不意味着不能通过其他途经修改(直接给对象赋值(更改数据)/ 绑定到普通引用)
对const的指针:
- 与普通指针的区别:
- 初始化时,指向的对象类型可以为非const
- 不允许通过指针*p=,改变指向的对象的值
const 指针:
- 与普通指针的区别:
- 定义时必须初始化
- 并不能改变指针指向的对象,但允许改变所指对象的值
指向const的const指针:
- ·不允许改变指向的对象的值,也不能改变指针指向的对象,
- 顶层 / 底层:
- 顶层const:本身是常量
- 底层const:所指向的对象是常量
- 可以即是顶层又是底层
- 内存:静态(存放生命周期为整个程序的),堆(保存动态分配的对象),栈(自动的生命周期)
内置指针:
new运算符(手动管理动态内存):
- 在堆中分配内存,返回指向该动态分配对象的指针:int * p = new int;'
- 初始化对象:对象默认情况为默认初始化,可以用()直接初始化对象的数据
- 内存耗尽:用光了堆内存
- 释放内存:防止内存耗尽,delete:销毁指向的对象,释放对应的内存。对象的生命周期在被delete前一直存在
- 未定义行为 :delete必须释放动态分配 的内存,并且相同的指针值不能释放多次,否则行为将未定义
- **空悬指针:**当delete指针,虽热内存释放了,但指针仍保存着无效内存的地址,成为了未初始化的指针(无效指针)
- 如何避免空悬指针?重置指针:如果要保留指针,让指针指向nullptr,变为空指针
- 但是:当多个指针指向同一内存,重置仅针对这个指针有效
智能指针(自动管理动态内存):
shared_ptr<type> name:(允许多个指针指向同一对象)
- 初始化:默认初始化为空指针
- 最安全的分配动态内存:= make_shared<name>(val);返回指向,动态分配并初始化未val的对象 ,的shared_ptr.
- 引用计数:
- 当初始化另一个shared_ptr / 按值传递 / 作为返回值......计数器++;
- 当被赋值 / 销毁(例如局部定义的)......计数器 --;
- 当计数器 == 0,自动销毁(delete)指向的对象,释放对应的内存。
- 作用:适用于多所有者共享对象的场景
shared_ptr&& 普通指针
- shared_ptr<type> p(new type(val));:new动态分配返回的指针指向的对象,初始化shared_ptr(必须采用这种直接初始化形式,不能用=拷贝初始化,因为shared_ptr的构造是explicit的,同样作为return也必须显示初始化)
- 注意事项:当混合使用new返回的普通指针 *p 和 shared_ptr p 的智能指针,容易引发错误(空悬指针 / 未定义行为):
- 当shared_ptr接管了内置指针,可能会无意释放掉内置指针指向的动态分配的内存
- 不要用get()初始化智能指针 / 为智能指针赋值:
- get()(返回内置指针:指向智能指针管理的对象):当不能使用智能指针时传递内置指针
shared_ptr的其他操作:
- shared_ptr<type> p(p1)p是shared_ptr p1的拷贝,指向同一个对象
- shared_ptr<type> p(p1)p管理内置指针 p1所指向的对象,p1必须指向new动态分配的内存
- shared_ptr<type> p(u)p接管unique_ptr u的对象
- reset重置(q可选参数)(相当于内置指针赋值):指向新的对象(内置指针q指向的对象),注意同样会更新引用计数(释放p指向的对象)。当q不存在,会释放(delete)shared_ptr的对象
unique_ptr<type>name;(和普通指针&&shared_ptr都不同(多对一),只能一个unique_ptr独占一个对象(一对一)):
- 初始化时必须直接初始化:unique_ptr<type> p(new type(val));
- 不支持拷贝/赋值操作,除了可以对将要被销毁的unique_ptr拷贝/赋值操作
- 支持转移所有权(从一个unique_ptr到另一个unique_ptr):
- release()将指针置为空,并返回内置指针(如果不用来初始化/赋值 智能指针,要记得delete)
- reset(q)指向新的对象(指向内置指针q指向的对象)
- 当unique_ptr被离开作用域时,指向的对象也被销毁
- 作用:适用于独占所有权的场景
weak_ptr:弱的
- 初始化:通过shared_ptr初始化(make_shared)
- 弱引用:不会增加引用计数
- 检查目标是否存在:lock()如果存在返回shared_ptr
- 作用:避免shared_ptr可能引发的循环引用问题
为什么使用动态内存?
- 不知道需要使用多少对象
- 不知道对象需要的准确类型
- 在多个对象间共享同一数据(当对象销毁数据不会销毁):shared_ptr:例如对shared_ptr拷贝时,两个指针都指向同一对象
- 手动控制对象的生命周期:new