zoukankan      html  css  js  c++  java
  • Chromium智能指针使用指南

    Chromium智能指针使用指南


    0.1252017.11.05 23:26:44字数 2,052阅读 1,773

    什么是智能指针?

    智能指针是一种特殊类型的“局部对象”,表现如同裸指针,但是具备离开作用域(out of scope)时主动释放所指向对象的能力。因为C++没有垃圾回收机制,因此智能指针的特性显得非常重要。

    下面是最常用智能指针类型std::unique_ptr<>的例子:

      // 我们可以在构造std::unique_prt<>的时候传入指针
      std::unique_ptr value(base::JSONReader::Read(data));
      std::unique_ptr foo_ptr(new Foo(...));
      
      // ...或者使用reset()
      std::unique_ptr bar_ptr;      // 与 "Bar* bar_ptr = nullptr;" 相似.
      bar_ptr.reset(new Bar(...));  // 此时 |bar_ptr| 不为空且持有对象 
      
      // 我们可以用 () 检查std::unique_ptr<>是否为空
      if (!value)
        return false;
      
      // get() 访问持有的裸指针
      Foo* raw_ptr = foo_ptr.get();
      
      // 我们可以像使用裸指针一样调用std::unique_ptr<>的方法
      DictionaryValue* dict;
      if (!value->GetAsDictionary(&dict))
        return false;
    

    为什么我们要使用智能指针?

    即使对象的创建和析构时机不确定,使用智能指针确保我们能正确释放对象。无论方法里有再多且逻辑复杂的路径,智能指针总能确保局部变量正确的释放,且能明确对象的所有权,避免程序内存泄漏或者对象重复释放。最后,在方法调用时,需要明确指出对象拥有权的转移和结果。

    存在哪些类型的智能指针?

    在Chromium里最常用的两种智能指针类型是std::unique_ptr<>scoped_refptr<>。前者适用于单一所有权的对象,后者适用于引用计数的对象(然而,通常应该避免使用引用计数的对象)。如果你比较熟悉C++11,会发现scoepd_refptr<>std::shared_ptr<>用法很相似。

    base/memory/ 还定义了其余几种类型的对象:

    • linked_ptr<> 用于在C++11之前存放智能指针对象,已被废弃。现在Chromium已经支持C++11了,我们不应该再使用linked_ptr<>了,而应该在STL容器里使用std::unique_ptr<>
    • ScopedVector<>也被废弃了。它是一种vector,并且持有容器内对象的所有权。请使用std::vector<std::unique_ptr<>>代替。
    • WeakPtr<>实际上不是智能指针。它的表现像指针类型,但是并不能用来自动释放对象,通常用作追踪其它地方拥有的对象是否依然存活,当追踪对象释放时,WeakPtr<>会自动的置为null。(但是依然需要在解引用前判断是否为null,因为解引用null WeakPtr<>等于于解引用null,而不是no-op。)WeakPtr<>与C++11的std::weak_ptr<>作用比较相似,但是使用了不同的API并且少了许多使用限制。

    如何选择使用哪种智能指针?

    • 单一所有权的对象。使用std::unique_ptr。需要注意的是,std::unique_ptr持有的需要必须是非引用计数的,并且分配在堆上的对象。
    • 无所有权的对象。使用裸指针或者WeakPtr<>。注意WeakPtr<>只能在创建它的线程解引用(通常使用WeakPtrFactory<>)。如果你需要在对象释放前后立刻执行某些操作,那么可能使用callback或notification更适合,而不是WeakPtr<>
    • 引用计数的对象。使用scoped_refptr<>,但是最好是重新考虑使用引用计数对象是否合理。引用计数对象很难明确拥有权和析构顺序,特别是在多线程环境中。总是有方法来重新设计引对象层级来避免引用计数的。限制每个类都只能在单个线程工作,并且使用PostTask()确保调用在正确的线程,这样有助于在多线程中避免引用计数。base::Bind()WeakPtr<>等工具具备在对象释放时自动取消方法调用的能力。Chromium中依然有许多代码在使用引用计数对象,如果你看见Chromium中有代码这样做但并不代表这是合理的解决方案。
    • 平台特定类型。使用平台特定的对象,譬如base::win::ScopedHandlebase::win::ScopedComPtr,或者base::mac::ScopedCFTypeRef。需要注意的是这些类型使用方式可能和std::unique_ptr<>不同。

    不同类型指针间调用规定是怎样的?

    calling conventions section of the Chromium style guide有规定。下面列出一些常用的规定。

    • 如果方法参数里使用std::unique_ptr<> ,说明该方法需占用传入参数的所有权,调用方需要使用std::move()来表明转移对象的所有权。需要注意的是,临时对象不需要调用std::move()转移所有权。

        // Foo() 拥有 |bar| 的所有权.
        void Foo(std::unique_ptr<Bar> bar);
        
        ...
        std::unique_ptr<Bar> bar_ptr(new Bar());
        Foo(std::move(bar_ptr));          // 调用后,|bar_ptr| 被置为 null.
        Foo(std::unique_ptr<Bar>(new Bar()));  // 临时对象不需要调用std::move()
      
    • 如果方法的返回值使用std::unique_ptr<>,说明调用方需要持有返回对象的所有权。这种情况下,当且仅当返回对象类型和临时对象的类型不同时,需要使用std::move()

        class Base { ... };
        class Derived : public Base { ... };
        
        // Foo 拥有|base|的所有权, 调用方拥有 返回值对象 的所有权
        std::unique_ptr<Base> Foo(std::unique_ptr<Base> base) {
          if (cond) {
            // 转移 |base| 的所有权给调用方
            return base;                           
          }
          
          // 注意这种场景下,方法运行结束时,|base|会被释放掉
          if (cond2) {
            // 临时对象不需要调用std::move()
            return std::unique_ptr<Base>(new Base()));  
          }
          std::unique_ptr<Derived> derived(new Derived());
          // 注意需要使用std::move(),因为|derived|的类型和返回值的类型不同。
          return std::move(derived);
        }
      
    • 如果方法传入或者返回裸指针,表示无需所有权转移。Chromium在std::unique_ptr<>存在之前写的一些代码,或者不熟悉所有权转移的程序员写的代码,可能会在传入或者返回裸指针的时候也使用std::move()转移了所有权。但是这样做是不安全的,编译器并不能执行正确的表现。去掉这样的代码吧,方法传入或者返回裸指针时,绝对不要转移所有权。

    可以通过引用传递参数或者返回值吗?

    不要这样做。

    原理上来说,传入const std::unique_ptr<T> &参数并且不转移所有权比传入T*有优势,这样做可以防止调用方传入错误的参数(譬如把 int 转成了 T*),而且调用方必须确保方法调用周期内传入对象不会被释放。但是,这样调用方就必须把传入对象生成在堆上,即使调用方原本可以使对象生成在栈上。这里传入裸指针相比传入const std::unique_ptr<T> &的好处是,可以将对象所有权的问题和对象生成的问题解耦。为了简洁和统一,我们避免开发人员去权衡这些利弊,总是使用裸指针就好了。

    有个例外,在lambda表达式中,若将智能指针放在STL容器里作为参数传递,这里为了编译通过,必须使用const std::unique_ptr<T> &

    我想使用STL容器用来持有指针对象。此时可以用智能指针吗?

    可以。在C++11里,你可以将智能指针放入STL容器内。而且,不要再使用ScopedVector<T>了,使用std::vector<std::unique_ptr<T>>来替代。同样的,再也不要再使用linked_ptr<T>了,直接把智能智能放在STL容器里使用即可。

    引用资料

  • 相关阅读:
    洛谷P1272 重建道路
    uva 10004
    uva10305
    uva 10044
    uva 120
    uva699
    uva297
    uva11111
    uva 101
    uva10152 ShellSort
  • 原文地址:https://www.cnblogs.com/bigben0123/p/12911975.html
Copyright © 2011-2022 走看看