说起内存管理,大多数人第一反应就是new,delete等操作,这一类操作所针对的对象就是堆对象,在使用这一类操作时,一个很突出的问题就是内存泄露,这也是每个C++程序猿的挥之不去的噩梦,曾几何时因为一个不起眼的指针释放问题,而导致了服务器因内存写满而宕机,而如今这类问题,对于C++程序开发者而言已经不再是问题了,为了解决这个问题,C++程序员采用了RAII机制(资源获取即初始化),具体思想就是:在对象初始化时分配资源,而在对象释放时,则释放资源。这类思想的最直接的实现方式就是采用智能指针,下面我们就来看看在boost中的一些比较常用的智能指针吧,在boost.smart_ptr库中主要包括了六种智能指针,分别是:1.scoped_ptr,2.scoped_array,3.shared_ptr,4. shared_array,5.weak_ptr,6.intrusive_ptr,接下来我们就来看看这六种智能指针的是使用方法吧,在使用这类指针之前,我们需要包含的头文件<boost/smart_ptr.hpp>
1. scoped_ptr智能指针
scoped_ptr智能指针最大的特点就是对分配对象的所有权十分严格,并且不允许转让所有权,换句话说一旦scoped_ptr获取了对象的管理权时,你就不能将该对象的管理权取回, 接下来看看scoped_ptr的类的声明吧,代码如下:
template<class T>
class scoped_ptr{
public:
explicit scoped_ptr(T* p = 0);
~scoped_ptr();
void reset(T* p = 0);
T& operator*() const;
T* operator->() const;
T* get() const;
void swap(scoped_ptr& b);
private:
T* ptr;
scoped_ptr(scoped_ptr const&);
scoped_ptr& operator = (scoped_ptr const&);
};
从它的头文件中,我们可以看出来,scoped_ptr类是不能被复制,从而保证了其所管理的对象不会转让给其他的指针,并且在类的 成员变量中,我们看到了被管理的原始对象指针,这个指针主要是在scoped_ptr构造函数中被初始化,并且在析构函数中被释放,
scoped_ptr指针不支持比较操作,不能在scoped_ptr对象之间进行相等或不相等的比较操作,下面我们就来看看scoped_ptr智能指针的具体用法吧,代码如下:
#include <boost/smart_ptr.hpp>
#include <iostream>
using namespace boost;
using namespace std;
struct posix_file
{
posix_file(const char* file_name)
{
cout<<"open file:"<<file_name<<endl;
}
~posix_file()
{
cout<<"close file"<<endl;
}
void display()
{
cout<<"display function"<<endl;
}
};
int main()
{
scoped_ptr<int> p(new int);
if(p)
{
*p = 1000;
cout<<*p <<endl;
}
p.reset();
assert(p==0);
scoped_ptr<posix_file> fp (new posix_file("tmp/a.txt"));
fp->display();
return 0;
}
测试结果:
1000
open file:tmp/a.txt
display function
close file
接下来,我们来看看scoped_ptr指针与auto_ptr指针的区别吧,其实scoped_ptr指针与auto_ptr指针最大的区别就是在对象的管理权上,scoped_ptr指针所管辖的对象管理权是不能转让的,而auto_ptr则可以转让。
2. scoped_array智能指针
scoped_array智能指针与scoped_ptr指针的最大区别就是一个是管理对象数组,一个是管理单个对象,首先来看看scoped_array类的声明吧,代码如下:
template<class T>
class scoped_array
{
public:
explicit scoped_array(T* p = 0);
~scoped_array();
void reset(T* p = 0);
T& operator[](std::ptrdiff_t i) const;
T* get() const;
void swap(scoped_array& b);
};
scoped_array的主要特点:
1) 构造函数接受的指针必须是new[]的结果
2)没有重载*,->
3)析构函数使用delete[]来释放对象
4)提供operator[]操作符重载
5)没有begin(),end()等类似容器的迭代器
scoped_array智能指针的基本用法与scoped_ptr类似,唯一不同的就是scoped_array包装的是new[]产生的指针,而在析构时调用delete[],代码如下:
scoped_array<int> sa(new int[100]);
fill_n(&sa[0],100,5);
sa[10] = sa[20] + sa[30];
注意:在使用scoped_array指针时,需要特别注意operator[]操作,因为scoped_array指针不提供数组索引范围检查,如果使用时,超过了动态数组大小的索引时或使用负数作为索引时将引发未知行为,另外针对一般的动态数组的需求,建议使用vector,而非scoped_array,一个很简单的原因:scoped_array不支持动态扩展容器大小。
3. share_ptr智能指针
这个指针想必大家都很熟悉了,这个也是boost.smart_ptr中最有价值的智能指针了,share_ptr智能指针是一种基于引用计数型的智能指针,与scoped_ptr最大的区别就是它可以被自由的拷贝和复制,其可以安全地放置在标准容器中,下面就来看看这个无比强大的基于引用计数型的智能指针的类声明吧,代码如下:
template<class T>
class share_ptr
{
public:
typedef T element_type;
template<class Y> explicit share_ptr(Y* p);
template<class Y,class D> share_ptt(Y* p, D d);
~share_ptr();
share_ptr(share_ptr const& r);
template<class Y> explicit share_ptr(std::auto_ptr<Y>& y);
share_ptr& operator = (share_ptr const& r);
template<class Y> share_ptr& operator = (share_ptr<Y> const& y);
template<class Y> share_ptr& operator = (std::auto_ptr<Y>& y);
void reset();
template<class Y> void reset(Y* p);
template<class Y,class D> void reset(Y* p,D d);
T& operator*() const;
T* operator->() const;
T* get() const;
bool unique() const;
long use_count const;
void swap(share_ptr& b);
};
在share_ptr类中,其中有两个成员函数是对引用计数进行操作的,第一个是unique,这个函数的主要用途就是用于判别引用计数是否为1,而use_count这个函数则是用于测试,在share_ptr中还支持比较操作,不要是比较share_ptr所管理的对象是否一样,此外不支持大于小于的比较,另外,在share_ptr指针转换时,其提供了三个转换函数:static_pointer_cast,const_pointer_cast 以及 dynamic_pointer_cast,其用法类似C++中的三个相似的转换函数,只是其转换后的类型是share_ptr。下面就来看看几个简单的应用,代码如下:
class share_base
{
public:
share_base(){}
share_base(shared_ptr<int> p_):p(p_){}
void print()
{
cout <<"count:"<<p.use_count()<<"v="<<*p<<endl;
}
private:
shared_ptr<int> p;
};
void print_func(shared_ptr<int> p)
{
cout<<"count:"<<p.use_count()<<"v="<<*p<<endl;
}
class share_sub : public share_base
{
public:
share_sub(shared_ptr<int> p_):share_base(p_){}
void print()
{
cout<<"this is a sub class\n"<<endl;
}
};
int main()
{
shared_ptr<int> p(new int(100));
share_base p1(p),p2(p);
p1.print();
p2.print();
*p = 20;
print_func(p);
p1.print();
shared_ptr<share_sub> sst(new share_sub(p));
shared_ptr<share_base> sb = static_pointer_cast<share_base>(sst);
sb->print();
return 0;
}
测试结果:
count:3v=100
count:3v=100
count:4v=20
count:3v=20
count:4v=20
从测试结果中可以看出,share_ptr引用计数的变化,p1,p2和p是共享一个原始对象,在print_func中由于传参采用的是值传递的形式,引入了临时变量,因此引用计数为4,而离开了print_func,临时对象被删除了,所以引用计数又回到3,当使用static_pointer_cast时,调用了拷贝函数,从而使得引用计数增1。针对share_ptr的操作,我们再来看看几个比较典型的应用吧,代码如下:
1)使用make_shared工厂函数
template<class T,class ... Args>
share_ptr<T> make_shared(Args && ... args);
实例代码如下:
#include <boost/make_shared.hpp>
#include <iostream>
#include <vector>
using namespace boost;
using namespace std;
class Date
{
public:
Date()
{
year = month = day = 0;
}
Date(int year,int month, int day):year(year),month(month),day(day){}
Date(const Date& test)
{
year = test.year;
month = test.month;
day = test.day;
}
Date& operator = (const Date& test)
{
if(this == &test)
return *this;
//Date(test);
year = test.year;
month = test.month;
day = test.day;
return *this;
}
void display()
{
cout<<"year:"<< year <<"month: "<< month <<"day: "<<day<<endl;
}
private:
int year;
int month;
int day;
};
int main()
{
shared_ptr<Date> dat = make_shared<Date>(2014,2,11);
shared_ptr<vector<Date> > datvec = make_shared<vector<Date> >(10,Date(2014,2,11));
dat->display();
for(int i = 0;i<datvec->size();i++)
(*datvec)[i].display();
return 0;
}
运行结果:
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
2)桥接模式
桥接模式是一种结构型设计模式,它的主要思想就是将具体的实现细节给隐藏掉,已到达类之间的最小的耦合关系,scoped_ptr和share_ptr都可以实现桥接模式,但是share_ptr相对来说更加合适,因为它支持拷贝赋值操作,多说一点,桥接模式其实在实现开发中应用还是很广泛的,因为它最大的好处在前面已经提及到,隐藏实现,减少耦合,使得模块之间,类之间的封装性更好,下面就来看看如何用share_ptr来实现一个简单的桥接模式吧,代码如下:
#include <boost/make_shared.hpp>
#include <iostream>
#include <string>
using namespace std;
using namespace boost;
class bookImpl
{
public:
bookImpl(std::string bookname = std::string(),const int price = 0):bookname(bookname),price(price){}
~bookImpl(){}
void display()
{
cout<<"bookname:"<<bookname<<" "<<"price:"<<price<<" "<<endl;
}
private:
string bookname;
int price;
};
class book
{
friend class bookImpl;
public:
book():p(new bookImpl){}
book(shared_ptr<bookImpl> const& p_):p(p_){}
void print()
{
p->display();
}
private:
shared_ptr<bookImpl> p;
};
int main()
{
book b1;
b1.print();
shared_ptr<bookImpl> bl(new bookImpl("C++ design",20));
book b2(bl);
b2.print();
}
测试结果如下:
bookname: price:0
bookname:C++ design price:20
3)应用工厂模式
工厂模式是一种创建型设计模式,在上面的make_shared就是一个基于工厂模式的好例子,下面我们就来看看如何使用share_ptr来实现一个简单的工厂,在使用share_ptr指针来实现工厂模式之前,我们需要明白share_ptr实现工厂模式的原理,主要是通过修改工厂方法的接口,使其返回share_ptr对象,而非原始对象指针,好了,说了这些,接着看代码吧,代码如下:
#include <boost/smart_ptr.hpp>
#include <iostream>
#include <string>
using namespace boost;
using namespace std;
class book
{
public:
virtual void displayName() = 0;
virtual void displayPrice() = 0;
virtual void setBookName(const std::string& str) = 0;
virtual void setBookPrice(const int price) = 0;
protected:
virtual ~book(){}
};
class ComputerBook : public book
{
public:
ComputerBook(std::string bookname = std::string(),const int price = 0):bookname(bookname),price(price){}
~ComputerBook(){}
void displayName()
{
cout<<"bookname:"<<bookname<<endl;
}
void displayPrice()
{
cout<<"price:"<<price<<endl;
}
void setBookName(const std::string& bookname)
{
this->bookname = bookname;
}
void setBookPrice(const int price)
{
this->price = price;
}
private:
std::string bookname;
int price;
};
template<class T,class S,class H,class B>
shared_ptr<T> create(H l,B t)
{
return shared_ptr<S>(new S(l,t));
}
template<class T,class S>
shared_ptr<T> create()
{
return shared_ptr<S>(new S());
}
int main()
{
shared_ptr<book> p = create<book,ComputerBook,std::string,int>("C++ Design",10);
p->displayName();
p->displayPrice();
shared_ptr<book> p1 = create<book,ComputerBook>();
p1->displayName();
p1->displayPrice();
p1->setBookName("AddressBook");
p1->setBookPrice(20);
p1->displayName();
p1->displayPrice();
return 0;
}
测试结果:
bookname:C++ Design
price:10
bookname:
price:0
bookname:AddressBook
price:20
4. share_array智能指针
share_array类似share_ptr,它们之间不同的是:share_ptr封装的是new操作,而share_array封装的是new[]操作,下面我们来看看share_array的类声明吧,代码如下:
template<class T>
class share_array
{
public:
explicit share_array(T* p = 0);
template<class D> share_array(T* p,D d);
~share_array();
share_array(share_array const& r);
share_array& operator = (share_array const& r);
void reset(T* p = 0);
template<class D> void reset(T* p,D d);
T& operator[](std::ptrdiff_t) const() const;
T* get() const;
bool unquie() const;
long use_count() const;
void swap(share_array<T>& b);
...
}
接下来,我们就来看看share_array指针的基本用法吧,代码如下:
#include<boost/smart_ptr.hpp>
using namespace boost;
int main()
{
share_array<int> p (new int[100]);
share_array<int> p1 = p;
p[0] = 1000;
assert(p1[0] == 1000);
}
share_array使用起来很方便,但是也需要注意一点:share_array在使用operator[]时,是没有提供索引范围检查的,如果索引超过了动态数组的大小的话,将会发生不可预知的行为。
5. weak_ptr智能指针
这个智能指针主要是为了配合share_ptr而引入的,它不具有普通指针的行为,并且没有重载operator*和->,它的最大的作用就是协助share_ptr,下面就来看看weak_ptr类声明吧,代码如下:
template<class T>
class weak_ptr
{
public:
explicit weak_ptr();
template<class Y> weak_ptr(share_ptr<Y>const& r);
weak_ptr(weak_ptr const& r);
~weak_ptr();
weak_ptr& operator = (weak_ptr const& r);
long use_count() const;
bool expired() const;
share_ptr<T> lock() const;
void reset();
void swap(weak_ptr<T>& b);
...
};
weak_ptr智能指针最大的特点就是:它只能获得对象的观测权,而不能获得对象控制权,换句话说,weak_ptr指针的构造不能引起指针引用计数器增加,同样的在析构时,也无法导致计数器减少,下面就从代码中看看weak_ptr的特性吧,代码如下:
#include <boost/smart_ptr.hpp>
#include <iostream>
using namespace boost;
using namespace std;
int main()
{
shared_ptr<int> p (new int(100));
assert(p.use_count() == 1);
weak_ptr<int> w(p);
assert(p.use_count() == 1);
if(!w.expired())
{
shared_ptr<int> r2 = w.lock();
assert(p.use_count()==2);
}
assert(p.use_count() == 1);
p.reset();
assert(w.expired());
assert(!w.lock());
return 0;
}
在结束本篇博文之前,我们再来看看weak_ptr的一个小应用吧,这个应用就是通过this指针来获得自己的share_ptr指针,即使得对象自己管理自己,而weak_ptr指针则是通过观测this指针,其不会增加引用计数,在需要的时候,可以通过lock函数来返回一个符合要求的share_ptr供外界使用,代码如下:
#include <boost/smart_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <iostream>
using namespace boost;
using namespace std;
class self_shared: public enable_shared_from_this<self_shared>
{
public:
self_shared(int x):n(x){}
void print()
{
cout<<"self_shard:"<<n<<endl;
}
void setValue(const int x)
{
n = x;
}
private:
int n;
};
int main()
{
shared_ptr<self_shared> sp = make_shared<self_shared>(1000);
sp->print();
shared_ptr<self_shared> p = sp->shared_from_this();
p->setValue(100);
p->print();
return 0;
}
测试结果
self_shard:1000
self_shard:100
总结
本篇主要是分析了下boost库里的一些内存管理方面的东西,内容比较基础,在学习这方面的东西时,自己也试着实现了下其中的几个智能指针,有时间的话,将代码贴出来,在下篇博文中,我们来分析下boost中的pool这个库,这个库也是异常的强大,尽情期待吧。
作者:zmyer
目前有 1 条留言 访客:0 条, 博主:0 条 ,引用: 1 条
外部的引用: 1 条
- Boost之内存管理学习(二) | 求索阁