C++智能指针相关知识点

智能指针与异常

如果使用智能指针, 如果程序块过早的结束, 智能指针能保证在内存不再需要时进行释放. (特别是在有多个出口的函数中 -- 虽然应尽量避免设计这样的函数, 但凡事总有例外 -- 此时使用智能指针来自动释放内存是非常方便的)
对于异常处理来说, 考虑下面两个函数, 当程序发生异常时, 智能指针也能正确的释放内存.
  void f3()
  {
    int* p = new int(10);
    throw "some error infomations";
    delete p;
  }

void f4()
{
    std::shared_ptr<int> sp = std::make_shared<int>(10);
    throw "some error infomations";

使用智能指针时的注意事项

C++智能指针使用时需要注意的事项, C++11中已经废弃了 auto_ptr, 因此不再讨论其用法, 无特殊说明, 下面的事项对 auto_ptr 而言, 可能是不正确的.

(1) 不要把一个原生指针给多个智能指针对象管理, 对所有的智能指针对象都成立

    int* p = new int(2);
    std::shared_ptr<int> sp0(p);
    std::shared_ptr<int> sp1(p); // 错误, 不能将同一原始指针对象给两个智能指针对象管理

(2) 不要把 this 指针给智能指针对象, 对所有的智能指针对象(包括 auto_ptr)都成立, 下面的代码演示错误的使用方法  

        #define PRINT_FUN() printf("%s:%d\n", __FUNCTION__, __LINE__)
        
        class CTest{
        public:
            CTest(){};
            ~CTest(){
                PRINT_FUN();
            };
            void Run()
            {
                m_sp = std::shared_ptr<CTest>(this); // 错误, 当 CTest 对象释放时也会释放 m_sp , 此时会再次 delete CTest 对象. (析构函数中的打印消息可以看出会出现一个对象两次调用析构函数.)
            }
        private:
            std::shared_ptr<CTest> m_sp;
        };
        
        std::shared_ptr<CTest> sp(new CTest());
        sp->Run();
        或者这样写
        CTest t;
        t.Run();    

  (3) 不要在函数实参里创建智能指针对象

function ( shared_ptr<int>(new int), g( ) ); //有缺陷
可能的过程是先 new int, 然后调 g( ), g( )发生异常, shared_ptr<int> 没有创建, int内存泄露
推荐写法:
shared_ptr<int> p(new int());
f(p, g());

(4) 处理不是 new 创建的对象要小心. 如果确实需要这样做, 需要智能指针传递一个删除器, 自定义删除行为.

    int* pi = (int*) malloc(4);
    shared_ptr<int> sp(pi); // shared_ptr 析构时将调用 delete. 使用 malloc 分配内存, 用 delete 释放显然不对.

  (5) 不要使用 new 创建一个智能指针对象.如 new shared_ptr<T> : 本来 shared_ptr 就是为了管理指针资源的, 不要又引入一个需要管理的指针资源shared_ptr<T>*

  (6) 使用 dynamic_pointer_cast 进行转换(C++11 中已废弃 shared_dynamic_cast)

            class B
            {
            public:
                B(){};
                virtual ~B(){};
            };
            class D : public B
            {
            public:
                D(){};
                virtual ~D(){};
            };

            std::shared_ptr<B> sp(new D);
            B* b = sp.get();
            D* d = dynamic_cast<D*>(b);
        正确用法:
            std::shared_ptr<B> spb(new D);
            std::shared_ptr<D> spd = std::dynamic_pointer_cast<D>(spb);

  (7) 不要 memcpy 智能指针对象

shared_ptr<B> sp1(new B);
shared_ptr<B> sp2;
memcpy(&sp2, &sp1, sizeof(shared_ptr<B>)); //sp2.use_count()==1
很显然,不是通过正常途径(拷贝构造,赋值运算),引用计数是不会正确增长的。

(8) 智能指针对象数组的使用, 需要自定义释放器.

    shared_ptr 数组, std::shared_ptr<A> p(new A[10], std::default_delete<A[]>());
    std::unique_ptr<int[]>(new int[10], std::default_delete<int[]>());

  (9) 将智能指针对象作为函数参数传递时要小心, 如下面的代码, 当调用所在的表达式结束(即函数调用返回)时, 这个临时对象就被销毁了, 它所指向的内存也被释放.

      int* pa = new int(10); // 小心, 不是一个智能指针
      f(std::shared_ptr<int>(pa)); // 合法的, 但内存会被释放
      int a = *pa; // 错误, pa已经被释放, 但继续指向已经释放的内存, 从而变成了一个空悬指针, 现在试图访问 pa 的值, 其结果是未定义的
    应该这样使用:
      std::shared_ptr<int> sp(new int(10));
      f(std::shared_ptr<int>(sp)); // 调用拷贝构造函数, sp.use_count == 2

  (10) 当将一个智能指针对象(如 shared_ptr)绑定到一个普通指针时, 就将内存管理的责任交给了这个 shared_ptr. 此后就不应该使用内置指针来访问 shared_ptr 所指向的内存了.

  (11) 不能使用 delete 释放 get 返回的普通指针. get 函数的设计是为了向不能使用智能指针的代码传递一个普通指针, 应该减少 get 函数的调用.

  (12) 不要使用 get 返回的普通指针来初始化另一个智能指针, 或为另一个智能指针赋值. 显然如果这样做, 将导致两次释放相同的内存, 或者其中一个已经将内存释放, 但另一个还在使用.

std::shared_ptr<int> sp = std::make_shared<int>(10);
int* p = sp.get();
{
  std::shared_ptr<int> sp(p);
}
int x = *sp;

avatar

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: