本笔记记录C++在类设计方面的学习,与之前的类似STL,库函数,位运算,String等笔记主要侧重C++在算法实现上的应用不同,本篇笔记重点关注C++在面向对象上的提供的机制,以及如何设计Class的笔记,侧重于工程实现
One of the core tenets of C++ is to never give you the cost of anything you didn’t ask for.
Big Three & Resource Management
总的来说,Resource Management是一个比较麻烦事情,而且C++本身的线程安全也比较难以处理,这也是C++难以出现类似于Java中Spring一样的框架的原因之一
C++11之前常见的内存问题:dangling pointer, double free, memory leak
C++11的解决方案:
- dangling pointer, memory leak => weak_ptr(可以容忍资源outlive, non-owning),
- double free => shared_ptr(owning)
- 使用weak_ptr的前提是shared_ptr
- weak_ptr(可以容忍资源outlive, non-owning)
- 如果不想share,那就选择unique_ptr,
- 对于非资源的类型,使用raw pointer,例如,在list中,节点间的关系显然不存在owning的关系,而只是单纯的指向关系,这种场景就适合raw pointer
在C++中常用指针这个概念来管理资源,对于资源最基本的操作就是分配与回收,这时候的指针和C语言中最基本的指针含义有所不同,不再是单纯的地址变量,而是用于实现某一抽象资源的管理,在C++11之后,多用智能指针代替传统指针实现对资源的管理
明确class invariant指的是对象的所有member处于合法可用状态
copy,即对象复制,常见的按值传递参数就会调用copy constructor,对于具有resource management的类而言,需要手动实现big three,并遵循rule of three,
常见的资源类型包括heap memory,file handle,mutex,socket connection,database connection;除了heap memory,其他的资源都是noncopibale
对于具有noncopiable资源的对象,我们应该将其copy constructor和copy assignment operator设为delete,从而删除其复制行为,这貌似很激进的行为实际上是可以理解的,因为对于这种object,它不再是一个单一的值类型,我们是不会让它按值传递参数的(没有任何意义),很难想象按值拷贝一个socket connection会产生什么积极意义,可以理解为这种non-copiable资源应该需要每个object自行申请,而非从其他object上进行按值拷贝
我们将noncopiable资源默认设为不允许拷贝,在需要共享该资源时,考虑使用智能指针
在C++11中,可以将资源的生命周期管理权交给智能指针,智能指针实现了对共享资源的生命周期代理管理,例如,我们可以将共享资源交给shared_ptr进行封装管理,shared_ptr实现了引用计数,可以方便实现对共享资源的管理,例如在constructor中创建共享对象,并交给shared_ptr封装管理,然后在对象进行copy和copy assignment的时候,对shared_ptr进行复制时,就能激活引用计数,注意当不再需要改资源时,使用reset来让引用计数减一(在destructor)中定义即可
weak_ptr主要提供了lock()返回shared_ptr,以及expired()来验证资源的合法性(防止指针引用计数为0导致的非法性),(weak_ptr不会增加shared_ptr资源的引用计数),因为object自带一个shared_ptr,所以不可能非法,而在object之外使用,一方面可以知道当前共享资源的引用计数是否为0,如果不为0,可以返回一个临时shared_ptr(在out of scope时会被销毁,注意不要将这个shared_ptr再赋值给其他变量)
一个weak_ptr的典型应用场景就是将shared_ptr作为参数传递给lambda时,会造成内存泄漏(这个shared_ptr不会被释放),因此,应该传递weak_ptr,这样就不会增加引用计数
The weak_ptr is used when you or an object may temporarily use a shared_ptr while not intend to owning it(即不想增加引用计数)
Always use the standard smart pointers, and non-owning raw pointers.
non-owning,即所指资源的拥有权并不在自己身上,所以该资源的管理是属于其他object,所以我们不应该用智能指针指向这种并不由自己负责管理的对象,而是用raw pointer,并且不要对这样的指针进行delete操作
一个简单的判断是否拥有资源所有权的准则就是如果自己被销毁,该指针指向对象也应被销毁,例如在stl list中,如果list被销毁时,所有节点也应被销毁,则list对这些节点拥有所有权/责任,但我们删除某个节点时,其next指向的节点是不会被删除的,因为该节点并不拥有对next节点的所有权/责任