RAII 与 智能指针
作者:互联网
- C++11 的 for each只要实现了begin()与end()是可以使用,也就是list也可以用,而
for_each()
函数
RAII
所谓的RAII,全称为Resource Acquisition Is Initialization,汉语是“资源获取即初始化”。但是这个直译并没有很好地解释这个词组的含义。其实含义并不高深复杂,简单说来就是,在资源获取的时候将其封装在某类的object中,利用"栈资源会在相应object的生命周期结束时自动销毁"来自动释放资源,即,将资源释放写在析构函数中。所以这个RAII其实就是和智能指针的实现是类似的
- 如果没有destructor,那么如果程序有多个
return
,那么需要在return之前处理好资源的释放
- C++的异常处理没有finally语句,在异常发生时,C++会自动调用已创建对象的解构函数,而在JAVA中,虽然GC也会解构已创建对象,并且也可以在finally中迅速解构已创建对象,但是GC得过一段时间,这个时候对于有时序要求或者对性能有要求的程序,就会出错,比如mutex的死锁
初始化表达式
- 不用初始化表达式,缺点是,对于成员变量的初始化,先是初始化为空,再去赋值,也就是初始化了两遍,这样很低效;并且如果存在没有无参构造的成员变量,这样的写法会出错;并且对于const的成员变量,在初始化表达式中初始化就会没问题,在花括号里赋值,相当于给const变量赋值,是错误的
- 初始化表达式,
explicit
- 甚至可以这样构造类
- 但是为了防止出错,可以使用explicit,防止以上的初始化
- 一个不加explicit的错误的单参数的构造函数的例子
- 对于多参数,excplicit也有作用
{}与()调用构造函数的区别
{}调用构造函数会防止narrow_cast,也就是不准精度损失
POD(plain-old-data)陷阱
- 为了兼容c语言
- POD解决方案?
默认构造函数:无参数
- 对于零初始化,如果连{}都不写,那么变量的值就会使内存中随机的值
拷贝构造与拷贝赋值
拷贝赋值会将原来的对象中的数据销毁
C++11初始化列表
对于一个class中不存在相应构造函数,那么以()初始化的对象,会编译错误
有自定义构造函数时仍然想用默认构造函数:= default
当一个类有其他构造函数的时候,自动生成一个参数个数和成员一样的构造函数这个构造函数就没有了,并且对空参构造函数使用=deafault也不能恢复之前的那个构造函数,这很正常吧?
std::string 不支持拷贝构造吗?
支持的,这里只是做的假设
三五法则
- 当定义一个类时,我们显式地或隐式地指定了此类型的对象在拷贝、赋值和销毁时做什么。一个类通过定义三种特殊的成员函数来控制这些操作,分别是拷贝构造函数、赋值运算符(也就是拷贝赋值)和析构函数。
- 拷贝构造函数定义了当用同类型的另一个对象初始化新对象时做什么,赋值运算符定义了将一个对象赋予同类型的另一个对象时做什么,析构函数定义了此类型的对象销毁时做什么。我们将这些操作称为拷贝控制操作。
- 由于拷贝控制操作是由三个特殊的成员函数来完成的,所以我们称此为“C++三法则”。在较新的C++11标准中,为了支持移动语义,又增加了移动构造函数和移动赋值运算符,这样共有五个特殊的成员函数,所以又称为“C++五法则”。也就是说,“三法则”是针对较旧的C++98标准说的,“五法则”是针对较新的C++11标准说的。为了统一称呼,后来人们干把它叫做“C++ 三/五法则”。
内容
- 需要拷贝操作的类也需要赋值操作,反之亦然。
- 析构函数不能是删除的
- 如果一个类成员有删除的或不可访问的析构函数,那么其默认和拷贝构造函数会被定义为删除的。
- 如果一个类有const或引用成员,则不能使用合成的拷贝赋值操作。(无法默认构造的const成员的类 则该类就无默认构造函数)
如果一个类定义了解构函数,那么您必须同时(定义或删除)拷贝构造函数和拷贝赋值函数,否则出错。
之所以需要重定义或者删除拷贝赋值,拷贝赋值也是编译器自动生成的浅拷贝
这里是低效的拷贝赋值,因为需要析构原来的内容,但是方便,因为可以复用拷贝构造函数
但是这个placement new暂时不是很明白?
这是高效的拷贝赋值,在原有的内容上realloc,不用销毁原有的内容
移动语义
对于将亡值v2,这里仍然去使用?
一些会自动调用移动语义的情况
对于return甚至可以返回左值,return std::as_const(v2)
没太看懂?
这里是v1掉用拷贝构造一个临时的对象,在传递个v2的移动构造赋值
智能指针
C++98时,delete只是把new出来的内存标记为没有被使用,但是现在的指针p还是指向那块内存;
而智能指针,只需要p = nullptr就可以完成置空与delete两步操作,符合封装性思想
由于三五法则,unique_ptr删除了拷贝构造函数
如何传递unique_ptr到函数中:
如果想要使用move之后,还能够访问到原来的指针
解决方案是提前获取原始指针
不过得保证raw_p的存在时间不会超过p的生命周期
这个时候需要shared_ptr,unique_ptr由于复制之后会删除两遍,而shared_ptr会记录引用计数,引用计数为0才删除指针
可以用p.use_count()获取引用计数
而且p.func()调用的是shared_ptr本身的成员函数,p->func()是p指向的对象的成员函数,在图中的C
但是shared_ptr会存在循环引用的错误
使用weak_ptr解决该问题
lock成员函数:返回一个shared_ptr类型的指针
expired函数:判断指针所指的内存空间是否被释放掉/指针是否为空/是否还有shared_ptr指针指向weak_ptr指向的内存空间
子窗口不能“拥有”父窗口
当一个类具有unique_ptr作为成员变量时,其拷贝构造/移动会被删除,移动构造/移动都会不受影响
标签:初始化,RAII,指针,智能,C++,拷贝,构造函数,ptr,赋值 来源: https://www.cnblogs.com/mlmz/p/16034128.html