sharerd_ptr 实践,线程安全性,预定义宏以及FAQ [boost 1 49 0]

4 篇文章 0 订阅

Handle/body idiom

shared_ptr 一个通常用法是用来实现 handle/body 模式. handle/body模式可以避免在头文件中暴露实现.

例子 shared_ptr_example2_test.cpp  包含头文件shared_ptr_example2.hpp .头文件中使用 shared_ptr 保存了了一incomplete type , 从而隐藏了实现.当调用实例的成员函数时,需要完整类型.这部分内容出现在shared_ptr_example2_test.cpp 这个实现文件中.需要注意的是, 实现文件中出现的显式析构函数其实不是必须的. ~shared_ptr 不像 ~scoped_ptr 一样,需要一个 complete type.


线程安全

shared_ptr提供与内置类型一样的线程安全级别.一个shared_ptr实例可以被多个线程同时读.不同的shared_ptr实例(即使这些shared_ptr底层共同拥有同一个引用计数)可以被不同的线程同时写入(通过 reset , 或者 operator= 操作),

任何同时写的操作将会产生未定义行为.

示例:


shared_ptr<int> p(new int(42));

//--- Example 1 ---

// thread A
shared_ptr<int> p2(p); // reads p

// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe

//--- Example 2 ---

// thread A
p.reset(new int(1912)); // writes p

// thread B
p2.reset(); // OK, writes p2

//--- Example 3 ---

// thread A
p = p3; // reads p3, writes p

// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write

//--- Example 4 ---

// thread A
p3 = p2; // reads p2, writes p3

// thread B
// p2 goes out of scope: undefined, the destructor is considered a "write access"

//--- Example 5 ---

// thread A
p3.reset(new int(1));

// thread B
p3.reset(new int(2)); // undefined, multiple writes


自boost release 版本 1.33.0起, shared_ptr 在以下平台采用 lock-free 实现:

  • GNU GCC on x86 or x86-64;
  • GNU GCC on IA64;
  • Metrowerks CodeWarrior on PowerPC;
  • GNU GCC on PowerPC;
  • Windows.
如果你的程序是单线程的, 不需要链接到任何使用 shared_ptr 默认配置的库. 可以通过在整个工程内 #defineBOOST_SP_DISABLE_THREADS ,这样可以切换到shared_ptr 的 非原子引用计数实现.

[ 仅仅在某些地方定义 BOOST_SP_DISABLE_THREADS ,而不是整个工程范围内, 编译单元将违背 one definition rule (注 1),产生未定义行为.不管怎样,实现满足了一些编译单元使用非原子引用计数的需求.只不过,没有任何保障. ]


你可以通过定义宏 BOOST_SP_USE_PTHREADS 关闭lock-free 实现,使用通用的 pthread_mutex_t-based 实现.


FAQ

Q:   实际应用中有多种智能指针,平衡不同的场景.为什么智能指针库只提供一种实现?如果能够提供多种类型的智能指针,就可以通过实验来确定哪种更适合实际的手头工作了.这样不是很有用么?

A:       智能指针库的一个重要目标就是提供一种标准的共享所有权的指针.仅仅对于一种类型的指针对于库接口的稳定十分重要.因为不同的智能指针之间通常不能够互相操作,比如: 引用计数指针就不能与链表指针共享所有权.


Q:       为什么 shared_ptr 没有提供用于用户定制的模板参数?

A:        参数化并不是那么好用. shared_ptr 模板被精心设计用来满足一般的,没有大量定制的需求.也许未来会有一天,一个高度可配置的smart_ptr可能会非常易用,而且不容易出错.但是在那之前,shared_ptr仍旧是大量应用首选的智能指针.(对参数化智能指针感兴趣的可以阅读一些Modern C++ Design by Andrei Alexandrescu).


Q:        我仍旧不那么认为.默认参数可以隐藏参数化的复杂性.那么,为什么不那么做呢?

A:         参数化影响了实际的类型[注2].所以.


Q:         为什么不使用链表实现?

A:          链表会增加一个指针,但是链表并没有足够的好处来平衡这个问题.除此之外,实现链表的线程安全是比较昂贵的.


Q:            为什么shared_ptr不提供一个向 T*的自动转换?

A:              这种自动转换容易出错.

r

Q:             为什么shared_ptr 提供 use_count();

A:              用于调试.比如跟踪循环依赖.


Q:              为什么shared_ptr不完成一些复杂性需求?

A:               因为复杂的需求会限定shared_ptr的实现,使shared_ptr变得复杂,并且不会对shared_ptr的用户带来明显的好处.比如: 当面对严格的复杂的需求时,错误检查的实现可能会变得不一致.


Q:              shared_ptr为什么没有一个 release() 函数?

A:               除非shared_ptr拥有唯一所有权,shared_ptr不能放弃所有权.因为可能还会有其他拷贝释放指针.


shared_ptr<int> a(new int);
shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2

int * p = a.release();

// Who owns p now? b will still call delete on it in its destructor.
此外, release()返回的指针可能不能够被可靠地释放掉.因为源shared_ptr创建时,可能定制了 deleter.


Q:           为什么operator-> 是const, 而他的返回值是一个指向内部元素的非常量指针呢?

A:            以拷贝指针的方式浅拷贝[注3],包括原始指针,通常不会传递其常量性质.这么做也没有什么意义,因为把一个 const 指针赋值给一个 non-const 指针式被允许的, 之后就可以通过这个non-const 指针改动对象了. shard_ptr 就是最接近原始指针的对象.


注 1: one definition rule 用于防止重复定义变量,类,枚举,函数以及模板 定义. 相同编译单元只能有一份定义,不同编译单元可以有多份定义,但是必须保证一致.

注 2:    这里应该是指配置的参数实际上会操作shared_ptr的类型T.

注 3:    shallow copy:  当A拷贝B时,A和B指向同一块内存时,这种拷贝就是浅拷贝.





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值