前言
编写中…
Step1: 用对象管理资源
Step2: RAII原则来申请和释放资源
- 在构造的时候申请资源,在析构的时候释放资源
Step3: 管理好对象的生命周期
管理好对象的生命周期,对于单线程环境来说比较好办,注意几点就行了,但是对于多线程环境就比较难处理了。下面首先讲一些单线程环境下对象生命周期中需要注意的事项:
构造::
- 不要在构造函数中暴露this指针,典型的不要调用观察者或者访问者接口,因为这个时候对象还没有构造完,不应该被调用
析构:
- 不要在析构函数中抛出异常,异常会改变程序执行的顺序。
- 父类析构函数应该定义为virtual,否则子类独有的资源不会被释放.
拷贝构造:
调用拷贝构造的时机:
- 用一个对象构造另一个对象 Point fei();Point lun(fei);
- 参数传递使用传值的方式的时候
- 函数返回类型值的时候
- Point fei = lun;(不会调用赋值操作符,只会调用拷贝构造)
赋值:
首先回顾一下赋值操作符的四个要点:
- 检查是否为同一个地址
if(this == &rhs) return *this
- 参数为类型的常引用
const Feature& rhs
- 返回为
*this
- 在分配资源之前先回收清理原有的资源
1 | Point fei(); |
多线程下对象生命周期的管理
聚合资源与组合资源
结论:
- 使用对象来管理资源,使用RAII原则来申请和释放资源
- 对于聚合资源对象的类,使用依赖注入和shared_ptr来构造
- RAII(资源申请和释放更安全)
- 依赖注入(构造更块,调用安全)
依赖注入和控制反转
依赖注入是一种将对象传入到某个类中的(注入),而不是让这个类自己创建并存储该对象的技巧,可将其作为控制反转概念的一种更为特殊的形式。举个例子:1
2
3
4
5
6
7
8
9
10
11
12class RedisDataSource
{
RedisConnection* poDriver_;
Vector<OGRLayer*> layers_;
public:
RedisDataSource():
poDriver_(new RedisConnection("host=192.168.1.23 port=5432 db=2"))
{
/// 其他操作
}
}
上面的代码存在以下问题:
- 如果RedisConnection的构造函数发生了变化,或者修改了数据库的端口,那么就必须修改RedisDataSource来解决
- 从销量来看,RedisDataSource的构造过程较长,切每个RedisDataSource事例都需要创建一个RedisConnection对象
- RedisDataSource强依赖于RedisConnection的定义
然而如果采用依赖注入的方式:
1 | class RedisDataSource |
- RedisDataSource只需要前置生命RedisConnection即可,依赖性相对较低
- 可以由RedisDataSource的工厂来创建RedisConnection并注入到RedisDataSource中,工厂负责维护RedisDataSource状态的正确性
对于上面依赖注入的例子,我们将Redis链接当作是资源的话,用对象来管理资源没有问题(RedisConnection类),使用依赖注入的方式还可以让对个RedisDataSource实例公用一个RedisConnection对象。但是这就要保证共享的RedisConnection生命周期的安全,也就是上面的Step3的要求,具体点就是资源对象在正确的时候析构。
1 | class RedisDataSource : public DataSource |
1 | RedisConnection* poRedis = |
关于内存管理
C++中可能出现的内存问题:
- 缓冲区溢出
- 空悬指针/野指针
- 重复释放
- 内存泄漏
- 不配对的new/delete
- 内存碎片
关于类型安全
从指向父亲的指针转为指向孩子节点的指针
Reference
- 《c++ API设计》by Martin Reddy
- 《企业应用架构》by Martin Fowler
- 《effective c++》
- 《Linux多线程服务器编程》 by 陈硕