《Boost程序库完全开发指南——深入C++“准”标准库》读书笔记(一)
注:文字、程序片段大部分摘录自书中。
1.scoped_ptr
1).scoped_ptr所有权严格,不能转让,一旦scoped_ptr获取了对象的管理权,你就无法再从它那里取回来。
2).scoped_ptr对象生命期结束时,析构函数~scoped_ptr()会使用delete操作符回收资源。
3).reset()功能重置scoped_ptr,删除原来保存的指针,在保存新指针值。这个函数不应该被调用。
4).scoped_ptr不允许拷贝、赋值。下面的代码是错误的。
scoped_ptr<string> sp(new string("text"));
scoped_ptr<string> sp2 = sp; //error
sp.reset(new string("another new text")); //拥有新指针
5).不能用在标准容器里。
2.share_ptr
1).关于构造函数
(1).shared_ptr(shared_ptr const & r)从另外一个shared_ptr获得指针的管理权,同时引用计数加1,结果是两个shared_ptr共享一个指针的管理权;
(2).operator=赋值操作符可以从另外一个shared_ptr或auto_ptr获得指针的管理权,其行为同构造函数;
(3).shared_ptr(Y *p, D d)行为类似shared_ptr(Y *p),但使用参数d指定了析构时的定制删除器,而不是简单的delete。
2).shared_ptr的reset()函数作用是将应用技术减1,停止对指针的共享。但参数的reset()则类似相同形式的构造函数,原指针应用技术减1的同事改为管理另一个指针。
3).unique()在shared_ptr是指针的唯一所有者时返回true,是可靠的。
4).use_count()返回当前指针的引用技术,但有的时候是不可用的。
5).多态情况下:
(1).把一个基类指针转型为一个子类指针或者反过来,需要使用提供的转型函数:
static_pointer_cast<T>()
const_pointer_cast<T>()
dynamic_pointer_case<T>()
返回的是转型后的shared_ptr。下面是书中的例子:
//class bad_exception : public exception {};
shared_ptr<std::exception> sp1(new bad_exception("error"));
shared_ptr<bad_exception> sp2 = dynamic_pointer_cast<bad_exception>(sp1);
shared_ptr<std::exception> sp3 = static_pointer_cast<std::exception>(sp2);
6).shared_ptr支持流输出操作符operator<<,输出内部的指针值,方便调试。
3.make_shared<T>()
make_shared<T>()消除显式的new调用,创建一个shared_ptr<T>的对象并返回。书中的例子:
shared_ptr<string> sp = make_shared<string>("make_shared");
shared_ptr<vector<int> > spv = make_shared<vector<int> >(10, 2);
assert(spv->size() == 10);
4.weak_ptr
1).为配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,它最大的作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。
2).可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造函数不会引起指针引用计数的增加。析构时也不会导致计数的减少。
3).use_count()可以观测资源的引用计数。
4).expired()功能等同于use_count==0,但更快。
5).lock()从北观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。下面是书中的例子:
shared_ptr<int> sp(new int(10));
assert(sp.use_count() == 1);
weak_ptr<int> wp(sp);
assert(wp.use_count() == 1);
if (!wp.expired())
{
shared_ptr<int> sp2 = wp.lock();
*sp2 = 100;
assert(wp.use_count() == 2);
}
assert(wp.use_count() == 1);
sp.reset();
assert(wp.expired());
assert(!wp.lock());
5.bad_weak_ptr
微软的TR1提供的一个指针,作为当shared_ptr以一个weak_ptr类对象为参数的构造函数时抛出的一个异常。下面是优快云的例子:
// std_tr1__memory__bad_weak_ptr.cpp
// compile with: /EHsc
#include <memory>
#include <iostream>
int main()
{
std::tr1::weak_ptr<int> wp;
{
std::tr1::shared_ptr<int> sp(new int);
wp = sp;
}
try
{
std::tr1::shared_ptr<int> sp1(wp); // weak_ptr has expired
}
catch (const std::tr1::bad_weak_ptr&)
{
std::cout << "bad weak pointer" << std::endl;
}
catch (...)
{
std::cout << "unknown exception" << std::endl;
}
return (0);
}
6.pool库
1).explicit pool(size_t requested_size)构造函数指示每次pool分配内存块的大小(requested_size),pool会根据需要自动地向系统申请或归还使用的内存,在析构时,pool将自动释放它所持有的所有内存块。
2).malloc()行为类似C中的malloc(),用void*指针返回从内存池中分配的内存块,大小为构造函数中指定的requested_size。如果内存分配失败,函数返回0,不会抛异常。
3).分配后的内存块可以用is_from()函数测试时候从这个内存此分配出去的。下面是书中例子:
#include <boost/pool/pool.hpp>
using namespace boost;
int main()
{
pool<> pl(sizeof(int));
int *p = (int *)p1.malloc();
assert(p1.is_from(p));
p1.free(p);
for (int i = 0; i < 100; ++i) {
p1.ordered_malloc(10);
}
} //内存池对象析构,所有分配的内存在这里都被释放。
4).pool<>只需要注意一点,它只能作为普通数据类型入int、double等的内存池,不能应用于复杂的类和对象,因为它只分配内存,不调用析构函数,这个时候我们需要用object_pool。
7.object_pool
1).object_pool是用于类实例(对象)的内存池,它的功能与pool类似。但会在析构时对所有已经分配的内存块调用析构函数,从而正确地释放资源。模板如下:
template<typename ElementType>
class object_pool : protected pool
{...}
2).malloc()和free()函数分别分配和释放一块类型为ElementType*的内存块,但它们被调用时并不调用类的构造函数和析构函数,也就是说操作的是一块原始内存块,里面的值是未定义的,因此我们应尽量少使用。
3).construct()和destroy()这两个函数是object_pool真正价值所在。construct先调用malloc()分配内存,然后再在内存块上使用传入的参数调用类的构造函数,返回的是一个已经初始化的对象指针。destroy()则先调用对象的析构函数,然后再用free()释放内存块。下面是书中例子:
struct demo_class {
public:
int a,b,c;
demo_class(int x=1, int y=2, int z=3) : a(x), b(y), c(z){}
};
int main()
{
object_pool<demo_class> p1;
demo_class *p = p1.malloc();
assert(p1.is_from(p));
//p指示那个内存未经过初始化
assert(p->a!=1 || p->b!=2 || p->c!=3);
p = p1.construct(7, 8, 9);
assert(p->a == 7);
object_pool<string> pls;
for (int i = 0; i < 10; ++i) {
string *ps = pls.construct("hello object_pool");
cout<< *ps << endl;
}
}
8.assign
1).用于更方便的往STL容器填充数据。使用assign库必须使用using指示符,只有才能让重载的+=,等操作符在作用域内生效。assign仅限应用于STL中定义的标准容器(vector、list、set等)
#include <boost/assing.hpp>
int main()
{
using namespace boost::assign;
vector<int> v;
v += 1,2,3,4,5,6*6;
set<string> s;
s += "cpp","java","c#","python";
map<int, string> m;
m += make_pair(1, "one"), make_pair(2, "two");
}
2).assign提供三个辅助函数insert()、push_front()、push_back()。这些函数可用于拥有同名成员函数的容器,接受容器变量作为参数,返回一个代理对象list_inserter。书中例子:
#include <boost/assign.hpp>
int main()
{
using namespace boost::assign;
vector<int> v;
push_back(v)(1)(2)(3)(4)(5);
list<string> l;
push_front(1)("c++")("c#")("java")("python");
set<double> s;
insert(s)(3.14)(0.618)(1.732);
map<int, string> m;
insert(m)(1, "one")(2, "two");
}
甚至能写出一些可怕的代码:
deque<string> d;
push_front(d)() = "c++","c#","python";
3).在容器构造的时候就完成数据的填充,这种方式较赋值更为高效,assign使用list_of()、map_list_of()/pair_list_of()和tuple_list_of()三个函数进行这些处理。
vector<int> v = list_of(1)(2)(3)(4);
set<int> s = (list_of(10), 20, 30);
例子中的语法还是比较复杂,我觉得还是尽可能少用比较好。
9.singleton
1).singleton即单件模式,实现这种模式的类在程序生命周期里只能有一个且仅有一个实例。目前Boost中并没有专门的单件库,而仅是其他库中有并不十分完善的实现。下面介绍singletom_default。
template <typename T>
struct singleton_default
{
public:
typedef T object_type;
static object_type & instance();
};
singleton_default把模板参数T实现为一个单件类,唯一实例只能通过静态成员函数instance()访问。它运用了巧妙的技术,可以在main()运行之前就创建单件。对类型T的要求是有缺省(无参)构造函数,而且在析构函数时不能抛出异常,因为单件在main()前后构造和析构,如果发生异常会无法捕抓。下面是书中的例子:
#include <boost/pool/default/detail/singleton.hpp>
using boost::details::pool::singleton_default;
class point
{
public:
point(int a=0, int b=0, int c=0):x(a),y(b),z(c)
{cout<<"point ctor"<<endl;}
~point()
{cout<<"point dtor"<<endl;}
};
int main()
{
cout<<"main() start"<<endl;
typedef singleton_default<point> origin;
origin::instance().print();
cout<<"main() finish"<<endl;
}
2).另一个单件类boost.serialzation里的singleton。大体跟singleton_default相同,区别是serialzation的但单件实现在访问单件实例的成员函数分为了常对象和可变对象两个函数。这种区分是出于线程安全的考虑,常对象单件总是线程安全的,应为它不会改变内部状态,而可变对象单件则不是线程安全的,可能会发生线程竞争问题。
#include <boost/serialization/singleton.hpp>
using boost::serialization::singleton;
class point{...};
int main()
{
cout<<"main() start"<<endl;
typedef singleton<point> origin;
origin::get_const_instance().print(); //常对象
origin::get_mutable_instance().print(); //可变对象
cout<<"main() finish"<<endl;
}
关于单件模式还搜到些有趣的文章,如:
1.ACE vs Boost: Singleton的实现
http://hi.baidu.com/okeyes888/blog/item/1db64a356c4d3c95a71e123b.html
2.Double-Checked Locking: An Optimization Pattern for Efficiently Initializing and Accessing Thread-safe Objects
http://www.cs.wustl.edu/~schmidt/PDF/DC-Locking.pdf