Effective C++学习笔记(二)

资源管理

1.以对象管理资源

手动删除:

    Effective C++学习笔记(二)
 

      当factory函数发生异常,或者{...}代码段发生过早的return语句等提前跳出f()函数,则不会执行delete语句,从而造成内存泄露

auto_ptr:

      Effective C++学习笔记(二)
 
      获取对象时立即放进管理对象,管理对象利用析构函数确保资源被释放。由于auto_ptr被销毁时会自动删除所指之物,所以应避免多个auto_ptr指向同一个对象,为了防止这一个问题,auto_ptrs有一个性质:通过copying构造函数或copy assignment操作符复制他们是,它们会变为null,而复制之后的指针将获得该资源的唯一拥有权。

      Effective C++学习笔记(二)
 

tr1::shared_ptr :
Effective C++学习笔记(二)
 
      
      shared_ptr与auto_ptr很类似,但可以允许多个该指针指向同一个资源,其内部能持续追踪有多少个指针指向某笔资源,直到确保没有指针指向它时会自动删除。

      Effective C++学习笔记(二)
 

2.在资源管理类中小心copying行为

#include <iostream>
using namespace std;

class Lock
{
public:
    explicit Lock(int* pm): m_p(pm)
    {
        lock(m_p);
    }

    ~Lock()
    {
        unlock(m_p);
    }

private:
    int *m_p;
    void lock(int* pm)
    {
        cout << "Address = " << pm << " is locked" << endl;
    }

    void unlock(int *pm)
    {
        cout << "Address = " << pm << " is unlocked" << endl;
    }
};


int main()
{
    int m = 5;
    Lock m1(&m);
}
  运行结果:

    Effective C++学习笔记(二)
 
  m1获得资源时将之锁住,释放资源时将之解锁,若修改main()函数,

int main()
{
    int m = 5;
    Lock m1(&m);
    Lock m2(m1);
}
  此时运行结果发生改变,
    Effective C++学习笔记(二)
 
    我们发现发生了一次locked和两次unlocked,原因是生成m1时调用一次含有locked构造函数,并最后释放一次,生成m2时调用默认copying构造函数并释放一次,且发生的是浅拷贝,两次释放同一个资源,在对于对内存操作的代码中会造成程序崩溃。

    可以通过以下方法改善:
  •     通过构造私有的拷贝构造函数和赋值操作符来禁止此类操作;
    
private:
    Lock(const Lock&);
    Lock& operator= (const Lock&);
Effective C++学习笔记(二)
 
  
  •     通过继承uncopyable类;

class Lock: public Uncopyable

{…}

  •     使用shared_ptr,引用计数器

class Lock
{
public:
    explicit Lock(int *pm): m_p(pm, unlock){…}
private:
    shared_ptr<int> m_p;   //采用shared_ptr
}
  •    复制底部资源(深拷贝)
  •     转移控制权

class Lock
{
public:
    explicit Lock(int *pm): m_p(pm, unlock){…}
private:
    auto_ptr<int> m_p;    //采用auto_ptr
}


3.成对使用new和delete时要采用相同的形式
Effective C++学习笔记(二)
 
如下例子:

#include <iostream>
using namespace std;

class A {
public:
    A() {
        cout<<"constructor A"<<endl;
    }

    ~A() {
        cout<<"destructor ~A"<<endl;
    }
};

int main() {
    A* a1 = new A();   //生成一个指向A对象的指针
    A* a2 = new A[3];  //生成一个指向A对象的指针数组

    delete a1;    //释放a1
    delete [] a2;  //释放a2
    return 0;
}

    运行结果如下:

      Effective C++学习笔记(二)
 

    可知执行了4(1+3)次无参构造函数,4次析构函数,若修改主函数


int main() {
    A* a1 = new A();
    A* a2 = new A[3];

    delete  a1; 
    delete a2;  //指针数组释放不带[]
    return 0;
}
   运行结果如下:
Effective C++学习笔记(二)
 
   

  此时只调用2次(1+1)析构函数,只释放了指针数组内的第一个资源,再次修改主函数


int main() {
    A* a1 = new A();
    A* a2 = new A[3];

    delete [] a1;  //释放指针带[]
    delete [] a2;  //释放a2  
    return 0;
}
    运行结果如下:

      Effective C++学习笔记(二)
 

    此时能通过编译,但程序陷入死循环,一直调用析构函数,在释放指针时带[ ]程序发生异常

4.以独立语句将newed对象置入智能指针

  Effective C++学习笔记(二)
 

   对于上述函数,编译器执行的顺序可能为: new Widget , priority() ,  tr1::shared_ptr。此时若priority()函数发生异常,则此时new Widget对象并不能成功置入智能指针中,有可能会发生内存泄露,因此应改为以下方式:
    Effective C++学习笔记(二)