scoped_ptr,顾名思义,是被设计用来管理作用域内指针的声明周期的。在指针对象离开当前作用域后,scoped_ptr负责清理销毁指针对象,从而避免c++程序员管理内存的负担。
1、scoped_ptr用法
1)scoped_ptr
{
scoped_ptr<Foo> foo(new Foo("wee"));
} // foo goes out of scope, releasing the pointer with it.
{
scoped_ptr<Foo> foo; // No pointer managed.
foo.reset(new Foo("wee")); // Now a pointer is managed.
foo.reset(new Foo("wee2")); // Foo("wee") was destroyed.
foo.reset(new Foo("wee3")); // Foo("wee2") was destroyed.
foo->Method(); // Foo::Method() called.
foo.get()->Method(); // Foo::Method() called.
SomeFunc(foo.release());
// SomeFunc takes ownership, foo no longer manages a pointer.
foo.reset(new Foo("wee4")); // foo manages a pointer again.
foo.reset();
// Foo("wee4") destroyed, foo no longer manages a pointer.
} // foo wasn't managing a pointer, so nothing was destroyed.
2)scoped_ptr
{
scoped_ptr<Foo[]> foo(new Foo[100]);
foo.get()->Method(); // Foo::Method on the 0th element.
foo[10].Method(); // Foo::Method on the 10th element.
}
scoped_ptr是movable but not copyable语意的对象,所以在用scoped_ptr作为函数形参时,传递的函数实参必须调用其pass函数,否则编译器会报错,因为scoped_ptr对象本身不支持拷贝。
void TakesOwnership(scoped_ptr<Foo> arg) {
// Do something with arg
}
// 函数调用
scoped_ptr<Foo> ptr(new Foo);
TakesOwnership(ptr);//错误,编译失败,不支持拷贝
TakeOwnership(ptr.Pass());
同理,scoped_ptr在作为函数返回值的时候,只能通过临时对象或者调用pass函数的方式返回
scoped_ptr<Foo> CreateFoo() {
// No need for calling Pass() because we are constructing a temporary for the return value.
return scoped_ptr<Foo>(new Foo("new"));
}
scoped_ptr<Foo> PassThru(scoped_ptr<Foo> arg)
{
return arg.Pass();
}
scoped_ptr<Foo> ptr(new Foo("yay"));
scoped_ptr<Foo> ptr2 = CreateFoo();
scoped_ptr<Foo> ptr3 = PassThru(ptr2.Pass());
}
指针是C++多态实现的基础,scoped_ptr最大限度的实现了指针的行为。scoped_ptr在初始化时将子类的指针赋值给父类指针对象。
scoped_ptr<Foo> foo(new Foo());
scoped_ptr<FooParent> parent(foo.Pass());
在函数返回值中需要将子类scoped_ptr对象转换为父类scoped_ptr对象,需要调用PassAs<>() 函数
scoped_ptr<Foo> CreateFoo() {
scoped_ptr<FooChild> result(new FooChild());
return result.PassAs<Foo>();
}
注意:PassAs<>()不支持scoped_ptr
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是完全类型,如果是不完全类型,编译会报错,同时enum的命名能够很好的提示具体原因。
2)模板特化技巧
scoped_ptr的数组删除器就是上面的DefaultDeleter的一个偏特化版本:
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并且不定义:
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文件之中:
// 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) 桥接模式的使用
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_;
// ......
};
参考地址:
http://www.programlife.net/chrome-source-code-scoped_ptr.html