首页 > Chrome源码学习 > Chrome源码学习之scoped_ptr

Chrome源码学习之scoped_ptr

scoped_ptr是一个智能指针,Boost中实现了一份,用于管理指针(参考《设计模式之桥接模式 Boost::scoped_ptr》)。Chrome也自己实现了一份,但是比Boost实现的版本要更加复杂,前者支持数组,后者不支持数组(数组使用scoped_array)。本篇分析文件为src\base\memory\scoped_ptr.h,记录要点:

1. 将错误提前到编译器提示
错误越早发现越好,一些不合理的语句的使用如果能提前到编译器就检测出来自然是件好事,Chrome使用了enum技巧对一些语句的使用做了约束,如删除器DefaultDeleter的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <class T>
struct DefaultDeleter {
  DefaultDeleter() {}
  template <typename U> DefaultDeleter(const DefaultDeleter<U>& other) {
    enum { T_must_be_complete = sizeof(T) };
    enum { U_must_be_complete = sizeof(U) };
    COMPILE_ASSERT((base::is_convertible<U*, T*>::value),
                   U_ptr_must_implicitly_convert_to_T_ptr);
  }
  inline void operator()(T* ptr) const {
    enum { type_must_be_complete = sizeof(T) };
    delete ptr;
  }
};

使用sizeof要求T和U是完全类型(就是到当前为止已经知道了T的具体定义,用sizeof自然就会知道T的大小是多少),如果是不完全类型,编译会报错,同时enum的命名能够很好的提示具体原因。

2. 模板特化
模板特化有很多种情况,我们最常见的是将模板类型T特化为一个具体的类型,比如int,这是所谓的全特化。出了这种情况之外,模板特化还有如下的情况:
1. 特化为数组 T[]
2. 特化为指针、引用 T*、T&以及const T*、const T&等
3. 特化为一个类模板 vector
这些都是偏特化。
关于上面所提及的详细描述,可以参考文章:《C++类模板的三种特化

scoped_ptr的数组删除器就是上面的DefaultDeleter的一个偏特化版本:

1
2
3
4
5
6
7
8
9
10
template <class T>
struct DefaultDeleter<T[]> {
  inline void operator()(T* ptr) const {
    enum { type_must_be_complete = sizeof(T) };
    delete[] ptr;
  }
 
 private:
  template <typename U> void operator()(U* array) const;
};

注意最后那个private函数,重载了小括号操作符(),这个函数只有声明,没有定义,也就是说不能够对它进行调用。这里是为了防止任何类型不一致的数组指针尝试的转型操作。前段时间,微博上左耳朵耗子跟别人讨论一个话题比较热,他写了篇总结《C++的数组不支持多态》,是的,千万不要对数组指针进行转型之后对其施加delete []操作。因为delete []需要对每个数组成员调用析构函数,T array[i]通过*(array + i*sizeof(T))取得对象,如果sizeof(T)是经过转型的,那么你会在一个很有可能不可以callable的内存地址上进行call操作,等着发疯吧。具体可以阅读《More Effective C++》条款3——绝对不要以多态方式处理数组

3. 明确拒绝不需要的函数
写一个类,如果情况合适,编译器会自动给你添加一些函数,有:默认构造函数、默认析构函数、默认复制构造函数、赋值操作符,当然如果你自己声明了,编译器就不会添加了。有些地方复制行为是禁止的,就比如这里的scoped_ptr,Chrome声明了一个宏,表示不需要编译器多此一举,采取的方法是把他们声明为private并且不定义:

1
2
3
4
5
6
template <class T, class D>
class scoped_ptr_impl {
 public:
 // ......
 private:
 DISALLOW_COPY_AND_ASSIGN(scoped_ptr_impl);

DISALLOW_COPY_AND_ASSIGN的定义位于src\base\basictypes.h文件之中:

1
2
3
4
5
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&);               \
  void operator=(const TypeName&)

关于这一点,更多请参考《Effective C++》条款06:若不想使用编译器自动生成的函数,就该明确拒绝

4. 桥接模式的使用
桥接模式可以参考上一篇文章,文件中定义了一个scoped_ptr_impl,作为scoped_ptr的内部实现类。

1
2
3
4
5
6
7
8
9
10
11
template <class T, class D = base::DefaultDeleter<T> >
class scoped_ptr {
 public:
 // ......
 private:
  // Needed to reach into |impl_| in the constructor.
  template <typename U, typename V> friend class scoped_ptr;
  base::internal::scoped_ptr_impl<element_type, deleter_type> impl_;
 
  // ......
};

欢迎关注《Chrome源码学习》系列文章。本人不保证文章准确性,如有错误欢迎指出!
如果你喜爱我的文章,请点击文章末尾的分享按钮,选择分享各类社交网站,你的分享是对我最大的支持。


觉得文章还不错?点击此处对作者进行打赏!


本文地址: 程序人生 >> Chrome源码学习之scoped_ptr
作者:代码疯子(Wins0n) 本站内容如无声明均属原创,转载请保留作者信息与原文链接,谢谢!


更多



  1. 2013年5月12日12:11 | #1

    居然研究起源码来了~ 活儿不多?

    [回复]

    代码疯子 回复:

    @tanglei, 这个说多就多了~~偶尔看下罢了,最近又是忙得死 [em004]

    [回复]

  2. Alex
    2013年5月16日17:46 | #2

    “这里是为了防止任何类型不一致的数组指针尝试的转型操作”
    这句话不是很理解,能多解释一下吗?

    谢谢

    [回复]

    代码疯子 回复:

    @Alex, 试试下面这段代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    
    #include <iostream>
    using namespace std;
     
    class Base
    {
    public:
        virtual ~Base(){ cout << "~Base" << endl; }
    private:
        int a;
    };
     
    class Derived : public Base
    {
    public:
        ~Derived(){ cout << "~Derived" << endl; }
    private:
        int b;
    };
     
    template <class T>
    struct DefaultDeleter 
    {
        DefaultDeleter() {}
        inline void operator()(T* ptr) const
        {
            enum { type_must_be_complete = sizeof(T) };
            delete ptr;
        }
    };
     
    template <class T>
    struct DefaultDeleter<T[]> 
    {
        inline void operator()(T* ptr) const 
        {
            enum { type_must_be_complete = sizeof(T) };
            delete[] ptr;
        }
     
    private:
        //template <typename U> void operator()(U* array) const;
    };
     
    int main(int argc, char* argv[])
    {
        // OK
        //Derived *pDerivedArr = new Derived[10];
        //DefaultDeleter<Derived []> deleter;
        //deleter(pDerivedArr);
     
        //Derived *pDerivedArr = new Derived[10];
        // 如果取消41行的注释,那么下一行编译通不过
        // 注释掉就可以编译通过,但是隐藏了危险
        //DefaultDeleter<Base []> deleter;  
        //deleter(pDerivedArr);
     
        // 下面是会出错的
        // 不过VS2008下居然测试正常
        // 标准的话不行吧 去g++编译运行就会段错误 segmentation fault
        Derived *pDerivedArr = new Derived[10]; 
        Base *pBase = pDerivedArr;
        delete []pBase;
     
    	return 0;
    }

    [回复]

  1. 2013年5月8日23:14 | #1