必须要注意的 C++ 动态内存资源管理(一)——视资源为对象

必须要注意的 C++ 动态内存资源管理(一)——视资源为对象


一.前言
        所谓资源就是,一旦你用了它,将来必须还给系统。如果不这样,糟糕的事情就会发生。C++ 程序中最常见使用的资源就是动态分配内存(如果你分配了内存却忘记归还它,就会导致内存泄漏)。但是内存只是你必须管理的众多资源之一。其他常见的资源还有:文件描述器(file descriptors)、互斥锁(mutex locks)、图形界面中的字体和笔刷、数据库连接、以及网络sockets。无论哪种资源,重要的是,如果你不再使用它时,必须将它还给系统。


二.先举个栗子
        假设我们在做一个RPG小游戏,主人公Player在游戏中获得不同buff从而更换不同的武器,假定这里我们的武器就只有三种:Fist(拳头),Knife(刀),Gun(抢)。
        然后这三种武器继承自 class Weapon 基类。
        那么我们可以使用一个工厂函数(factory function)来提供我们不同的武器: Weapon* createWeapon(…) ;
        当你程序如此设计的时候,那么你的工厂类就主要负责生产了,而createWeapon的调用端就要有责任对获取的武器资源进行释放。举例如下:


void changeWeapon(params)
{
    Weapon* oldWeapon = playerWeapon;
    playerWeapon = createWeapon(params);
    ...
        delete oldWeapon;
}

        以上代码看起来妥当,但在若干情况下,可能导致该函数释放oldWeapon——或许因为”…”中的某个过早的return语句。如果这样一个return语句被执行;又或者是因为”…”中有异常抛出;这些情况发生吃,那么控制流一定就不会触及到delete语句;而之前申请的内存(oldWeapon)就无可避免的泄露了。
        当然了,谨慎的编写程序可以防止这一类错误。但是有个问题是必须值得思考的,随着业务逻辑的更改,这段代码很可能会在时间渐渐过去之后被修改。一旦软件开始接受维护,很有可能会有某些人添加return语句或者continue语句而未能全然领悟它对函数资源管理策略造成的后果。
        这时候,我们需要将资源来视作一个对象来看。当构造初始化的时候获取资源,当析构销毁的时候释放资源。而析构函数是依赖C++的自动调用机制,这样我们就可以确保资源被释放。

                                                                          以对象管理资源                        ——————Effective C++ 条款13


三.资源对象
        有了前面的解释,我们现在大概能够对为什么需要将资源当对象来看待有了理解。 下面我们来简单来介绍如何实现:
先将资源封装成类,完成资源的初始化加载,和释放。
template<typename T>
class res_ptr
{
public:
    typedef T _myType;
    typedef T* _myPType;
    res_ptr(_myPType p = nullptr) :pointer(p){  }
    ~res_ptr(){ delete pointer; }
private:
    _myPType pointer;
};

现在我们可以用上面这个模板类来管理上面的指针。不过,上面的代码还没有完成。
在资源管理中,有时候会出现这样的语句:
res_ptr<T>  p1;

...

res_ptr<T> p2 = p1;

也就是赋值操作,在不同的场景中赋值操作可能有不同的含义(有时候不一定是赋值,可能是函数传参,函数返回)

1.如果是图片类的资源,在赋值的时候应该是资源的拷贝。
2.如果是文件、临接设备这样的资源,在赋值的时候应该是资源支配权的转移。
3.如果是数据库连接,网络sokets这样的资源,在赋值的时候应该是增加资源的引用数,而当引用数为0的时候释放资源。
所以,以上不同的类型也决定着类中的拷贝控制函数。


关于以上三种情况如何实现拷贝控制函数,我们留到下节继续介绍。

https://blog.csdn.net/y1196645376/article/details/53005803

原文地址:https://www.cnblogs.com/leijiangtao/p/12059848.html