最近在学习opencv源码过程中,发现c++知识忘的差不过了,所以借这个机会在复习一下C++ STL编程。首先记录一下智能指针,在后面学习过程中,会用到智能指针。
1.auto_ptr智能指针
c++的智能指针是比较简单的,简单在于它无法记录当前系统是否还有其它地方引用当前对象,这样的话就无法智能的释放对象。只能在智能指针对象释放时,方能释放引用对象。现在Android中的sp,wp,结合RefBase类,可以实现引用计数。前提是被引用的对象需要继承RefBase,这里就不多说了。
- 构造函数
template<typename _Tp>
class auto_ptr
{
private:
_Tp* _M_ptr;
public:
/// The pointed-to type.
typedef _Tp element_type;// 这个_Tp就是初始化时,传进来的模板。
explicit
auto_ptr(element_type* __p = 0) throw() : _M_ptr(__p) { }//《1》
auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) { }//《2》
template<typename _Tp1>
auto_ptr(auto_ptr<_Tp1>& __a) throw() : _M_ptr(__a.release()) { }//《3》
//.......
}
如上面可以看到有三种构造函数,《1》是最常用的,进来就把被引用对象的指针赋给_M_ptr对象。《2》这个是同种智能指针,相互传递过程时用,实际需要时,最好使用这种。《3》有定义可以看到,此时模板变成了(_Tp1),但是原始的模板是( _Tp),这样可能会引起转化问题,这不是很安全,少用吧。
- 重载运算符“*”,“->","="
auto_ptr&
operator=(auto_ptr& __a) throw()
{
reset(__a.release());
return *this;
}
template<typename _Tp1>
auto_ptr&
operator=(auto_ptr<_Tp1>& __a) throw()
{
reset(__a.release());
return *this;
}
element_type&
operator*() const throw()
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return *_M_ptr;
}
element_type*
operator->() const throw()
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return _M_ptr;
}
如上面"="运算符,没看到有被引用对象的类型,所以一般看到智能指针是 atuto_ptr<int> p(new int);
,“=”运算符可以应用在不同auto_ptr对象转换和应用不同类型引用对象的转换。其它2个运算符,返回的都是被引用对象自己,很好理解额。
- 方法-release、reset、get
element_type*
release() throw()
{
element_type* __tmp = _M_ptr;
_M_ptr = 0;
return __tmp;
}
void
reset(element_type* __p = 0) throw()
{
if (__p != _M_ptr)
{
delete _M_ptr;
_M_ptr = __p;
}
}
element_type*
get() const throw() { return _M_ptr; }
release()方法,可以理解成释放当前auto_ptr指针,并返回之前被引用的对象。reset()方法是,重置被引用的地方,可以理解成更换主人了。get()方法即返回当前引用对象。
- 析构函数
~auto_ptr() { delete _M_ptr; }
就这么一行代码,很黄很暴力,所以要特别注意"最好不要多个智能指针引用一个对象,目前智能指针还没那么智能",
##2.测试代码
定义了一个cat类,实现了若干方法。
#include <iostream>
#include <auto_ptr.h>
using namespace std;
class cat {
public:
cat(int age,char *name):cat_age(age),cat_name(name) {
cout<<"cat "+cat_name+" in" <<endl;
}
void jiao(){
cout << "miaomiao" << endl;
}
void print_age(){
cout <<"cat age is " << cat_age << endl;
}
void print_name(){
cout << "cat name is:"<< cat_name << endl;
}
~cat(){
cout << "~cat" <<endl;
}
private:
int cat_age;
string cat_name;
};
上面是简单的cat类。
- demo1
int main() {
cat* cat1 = new cat(8,"xiao_liu");
cat* cat2 = new cat(16,"xiao_hua");
cat1->print_name();
cat1->print_age();
cat2->print_name();
cat2->print_age();
return 0;
}
上面由于没有手动delete cat1,cat2,所以肯定有内存泄露,从下方运行结果可以发现,没有打印析够函数中的log。
cat xiao_liu in
cat xiao_hua in
cat name is:xiao_liu
cat age is 8
cat name is:xiao_hua
cat age is 16
- demo2
int main() {
cat* cat1 = new cat(8,"xiao_liu");
cat* cat2 = new cat(16,"xiao_hua");
cat1->print_name();
cat1->print_age();
cat2->print_name();
cat2->print_age();
auto_ptr<cat> smart_cat1(cat1);
auto_ptr<cat> smart_cat2(cat2);
return 0;
}
与demo差别在于,使用智能指针引用cat1和cat2。可以看到下面打印结果。可以发现调用了2次析够方法。非常方便。
cat xiao_liu in
cat xiao_hua in
cat name is:xiao_liu
cat age is 8
cat name is:xiao_hua
cat age is 16
~cat
~cat
- demo3
int main() {
auto_ptr<cat> smart_cat1(new cat(8,"xiao_liu"));
auto_ptr<cat> smart_cat2(new cat(16,"xiao_hua"));
//一般auto_ptr使用应该在一开始如上面使用,避免对象外泄。后面方法都使用智能
//指针对象,如果先使用一个cat *指针指向新new出来的对象,如果在一些地方执行
//了delete操作那么auto_ptr引用的对象就为空,所以要特别注意。
smart_cat1->print_name();
smart_cat1->print_age();
smart_cat2->print_name();
smart_cat2->print_age();
cat * cat3 = smart_cat1->get();//如果要拿被引用对象,可以这样做。
return 0;
}
运行结果:
cat xiao_liu in
cat xiao_hua in
cat name is:xiao_liu
cat age is 8
cat name is:xiao_hua
cat age is 16
~cat
~cat