前言
C++ 标准模板库 STL(Standard Template Library) 一共给我们提供了四种智能指针:auto_ptr、unique_ptr、shared_ptr 和 weak_ptr,其中 auto_ptr 是 C++98 提出的,C++11 已将其摒弃,并提出了 unique_ptr 替代 auto_ptr。虽然 auto_ptr 已被摒弃,但在实际项目中仍可使用,但建议使用更加安全的 unique_ptr,后文会详细叙述。shared_ptr 和 weak_ptr 则是 C+11 从准标准库 Boost 中引入的两种智能指针。此外,Boost 库还提出了 boost::scoped_ptr、boost::scoped_array、boost::intrusive_ptr 等智能指针,虽然尚未得到 C++ 标准采纳,但是在开发实践中可以使用。
一 auto_ptr
auto_ptr 同样是 STL 智能指针家族的成员之一,由 C++98 引入,定义在头文件。其功能和用法类似于 unique_ptr,由 new expression 获得对象,在 auto_ptr 对象销毁时,他所管理的对象也会自动被 delete 掉。
下面是一些成员函数及其简介
- get:得到对象类管理的对象指针
- operator*:得到管理的对象
- operator->:等价于(*class).,用于获取对象的某个成员
- release:将管理对象的指针返回并设置类内指针为null
- operator= :相当于调用release之后将返回值构造为新的类初始化左边的类,Release and copy auto_ptr
- reset:若无参数直接释放当前对象否则用参数重新设置当前值,无返回值
下面是一个小例子
#include <iostream>
#include <memory>
int main()
{
/*
get:得到对象类管理的对象指针
operator*:得到管理的对象
operator->:等价于(*class).,用于获取对象的某个成员
release:将管理对象的指针返回并设置类内指针为null
operator= :相当于调用release之后将返回值构造为新的类初始化左边的类,Release and copy auto_ptr
reset:若无参数直接释放当前对象否则用参数重新设置当前值,无返回值
*/
std::auto_ptr<int> a;
a.reset(new int);//reset:当前对象否则用参数重新设置当前值,无返回值
*a = 1;
cout << "a:" << *a << endl;// operator*:得到管理的对象
*a = 2;
std::auto_ptr<int> b;
b = a;//operator= :相当于调用release之后将返回值构造为新的类初始化左边的类,
if (a.get())//get:得到对象类管理的对象指针
{
cout << "a:" << *a << endl;
}
cout << "b:" << *b << endl;
*b = 3;
int *c = b.release();//release:将管理对象的指针返回并设置类内指针为null
if (a.get())
{
cout << "b:" << *b << endl;
}
*c = 4;
cout << "c:" << *c << endl;
std::auto_ptr<int> d;
d.reset(c);
*d = 5;
if (d.get())
{
cout << "d:" << *d << endl;
}
cout << "c:" << *c << endl;
d.reset();//reset:若无参数直接释放,无返回值
if (d.get())
{
cout << "d:" << *d << endl;
}
getchar();
return 0;
}
二.unique_ptr
unique_ptr 由 C++11 引入,旨在替代不安全的 auto_ptr。unique_ptr 是一种定义在头文件中的智能指针。它持有对对象的独有权——两个unique_ptr 不能指向一个对象,即 unique_ptr 不共享它所管理的对象。它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL)算法。只能移动 unique_ptr,即对资源管理权限可以实现转移。这意味着,内存资源所有权可以转移到另一个 unique_ptr,并且原始 unique_ptr 不再拥有此资源。实际使用中,建议将对象限制为由一个所有者所有,因为多个所有权会使程序逻辑变得复杂。因此,当需要智能指针用于存 C++ 对象时,可使用 unique_ptr,构造 unique_ptr 时,可使用 make_unique Helper 函数。
相比于auto_ptr,其可以在编译期间合理确定智能指针对象作为右值时的合法性,只有作为临时右值才合法否则报错
unique_ptr<int> getUnique(int parm)
{
unique_ptr<int> temp(new int(parm));
return temp;
}
int main()
{
unique_ptr<int> a; //创建空智能指针
a.reset(new int); //绑定动态对象
*a = 1;
cout << "a:" << *a << endl;
*a = 2;
unique_ptr<int>b = std::move(a);//所有权转移(通过移动语义),u_s所有权转移后,变成“空指针”
if (a.get())
{
cout << "a:" << *a << endl;
}
cout << "b:" << *b << endl;
*b = 3;
unique_ptr<int> c;
c.reset(b.release());//所有权转移
if (b.get())
{
cout << "b:" << *b << endl;
}
cout << "c:" << *c << endl;
//unique_ptr<int> d = c; //编译出错,已禁止拷贝
//unique_ptr<int> d(c); //编译出错,已禁止拷贝
//unique_ptr<int> d;
//d = c;//编译出错,已禁止赋值
unique_ptr<int> d = getUnique(1);//临时正确
getchar();
return 0;
}
三 shared_ptr
shared_ptr 是一个标准的共享所有权的智能指针,允许多个指针指向同一个对象,定义在 memory 文件中,命名空间为 std。shared_ptr最初实现于Boost库中,后由 C++11 引入到 C++ STL。shared_ptr 利用引用计数的方式实现了对所管理的对象的所有权的分享,即允许多个 shared_ptr 共同管理同一个对象。像 shared_ptr 这种智能指针,《Effective C++》称之为“引用计数型智能指针”(reference-counting smart pointer,RCSP)。
shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针,当然这需要额外的开销:
(1)shared_ptr 对象除了包括一个所拥有对象的指针外,还必须包括一个引用计数代理对象的指针;
(2)时间上的开销主要在初始化和拷贝操作上, * 和 -> 操作符重载的开销跟 auto_ptr 是一样;
(3)开销并不是我们不使用 shared_ptr 的理由,,永远不要进行不成熟的优化,直到性能分析器告诉你这一点。
下面是一些成员函数及其简介
- swap:交换对象的指针
- reset:重置指针
- get:返回保存的指针
- operator*:解引用,得到对象
- use_count:引用计数
- unique:当前引用技术是否为1
- operator bool:检查是否为空
shared_ptr 没有release函数。
make_shared函数:
最安全的分配和使用动态内存的方法就是调用一个名为make_shared的标准库函数,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。头文件和share_ptr相同,在memory中
int main()
{
shared_ptr<int> a = make_shared<int>(1);
std::shared_ptr<int> b(new int(2));
cout << "a:" << *a << endl;
cout << "b:" << *b << endl;
a.swap(b);//交换a和b
cout << "a:" << *a << endl;
cout << "b:" << *b << endl;
cout << "a:Count:" << a.use_count() << endl;
cout << "b:Count:" << b.use_count() << endl;
std::shared_ptr<int> c = a;//赋值
cout << "c:" << *c << endl;
cout << "a:Count:" << a.use_count() << endl;
cout << "b:Count:" << b.use_count() << endl;
cout << "c:Count:" << c.use_count() << endl;
*b = 3;
if (b.unique())//引用技术是否为1
{
cout << "b:" << *b << endl;
}
a.reset();//a 的引用计数为0 但是c还是可以使用
cout << "a:Count:" << a.use_count() << endl;
cout << "b:Count:" << b.use_count() << endl;
cout << "c:Count:" << c.use_count() << endl;
a.reset(new int(4));
cout << "a:Count:" << a.use_count() << endl;
cout << "b:Count:" << b.use_count() << endl;
cout << "c:Count:" << c.use_count() << endl;
getchar();
return 0;
}
实现自己的智能指针
template <typename T> class Smarter;
//辅助类
template <typename T> class Assistant
{
private://全部私有
friend class Smarter<T>;//智能指针为友元,可以访问私有成员
Assistant(T* p) :data(p), nCount(1) {}
~Assistant() { delete data; }
T* data;
int nCount;
};
template <typename T> class Smarter
{
public:
Smarter() {};
Smarter(T* p) :pAssistant(new Assistant<T>(p)) {}
Smarter(const Smarter<T> &p) :pAssistant(p.pAssistant)//拷贝构造
{
++pAssistant->nCount;
}
Smarter& operator=(const Smarter<T> &p)//赋值构造
{
++p.pAssistant->nCount;
if (pAssistant && --pAssistant->nCount == 0)
delete pAssistant;
pAssistant = p.pAssistant;
return *this;
}
T& operator* ()
{
return *(pAssistant->data);
}
T* operator->()
{
return pAssistant->data;
}
~Smarter()
{
std::cout << "nCount:" << pAssistant->nCount << std::endl;
if (--pAssistant->nCount == 0)
{
std::cout << "即将销毁" << std::endl;
delete pAssistant;
}
}
int count()
{
if (pAssistant)
return pAssistant->nCount;
return 0;
}
private:
Assistant<T> *pAssistant;
};
int main()
{
int *a = new int(1);
{
Smarter<int> b = a;
{
Smarter<int> c = b;
{
Smarter<int> d;
d = c;
cout << "a:" << *a << endl;
}
cout << "a:" << *a << endl;
}
cout << "a:" << *a << endl;
}
cout << "a:" << *a << endl;
getchar();
return 0;
}
四std::weak_ptr
weak_ptr 被设计为与 shared_ptr 共同工作,可以从一个 shared_ptr 或者另一个 weak_ptr 对象构造而来。weak_ptr 是为了配合 shared_ptr 而引入的一种智能指针,它更像是 shared_ptr 的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载 operator* 和 operator-> ,因此取名为 weak,表明其是功能较弱的智能指针。它的最大作用在于协助 shared_ptr 工作,可获得资源的观测权,像旁观者那样观测资源的使用情况。观察者意味着 weak_ptr 只对 shared_ptr 进行引用,而不改变其引用计数,当被观察的 shared_ptr 失效后,相应的 weak_ptr 也相应失效。
下面是一些成员函数及其简介
- weak_ptr w; //创建空 weak_ptr,可以指向类型为 T 的对象
- weak_ptr w(sp); //与 shared_ptr 指向相同的对象,shared_ptr 引用计数不变。T必须能转换为 sp 指向的类型
- w=p; //p 可以是 shared_ptr 或 weak_ptr,赋值后 w 与 p 共享对象
- w.reset(); //将 w 置空
- w.use_count(); //返回与 w 共享对象的 shared_ptr 的数量
- w.expired(); //若 w.use_count() 为 0,返回 true,否则返回 false
- w.lock(); //如果 expired() 为 true,返回一个空 shared_ptr,否则返回非空 shared_ptr
int main()
{
std::shared_ptr<int> a;
a.reset(new int(1));
std::weak_ptr<int> b = a;
cout << "a:Count:" << a.use_count() << endl;
cout << "b:Count:" << b.use_count() << endl;
std::shared_ptr<int> c;
if (!b.expired())//检查是否过期
{
c = b.lock();
*c = 2;
}
cout << "a=" << *a << endl;
cout << "c=" << *c << endl;
cout << "a:Count:" << a.use_count() << endl;
cout << "b:Count:" << b.use_count() << endl;
cout << "c:Count:" << c.use_count() << endl;
cout << endl;
std::weak_ptr<int> d = b;
cout << "a:Count:" << a.use_count() << endl;
cout << "b:Count:" << b.use_count() << endl;
cout << "c:Count:" << c.use_count() << endl;
cout << "d:Count:" << d.use_count() << endl;
cout << endl;
b.reset();
cout << "a:Count:" << a.use_count() << endl;
cout << "b:Count:" << b.use_count() << endl;
cout << "c:Count:" << c.use_count() << endl;
cout << "d:Count:" << d.use_count() << endl;
getchar();
return 0;
}
weak_ptr 的作用
现在要说的问题是,weak_ptr 到底有什么作用呢?从上面那个例子看来,似乎没有任何作用。其实 weak_ptr 可用于打破循环引用。引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象。一个简单的例子如下:
class Woman;
class Man
{
private:
std::weak_ptr<Woman> m_wife;
//std::shared_ptr<Woman> m_wife;
public:
void setWife(std::shared_ptr<Woman> woman)
{
m_wife = woman;
}
~Man()
{
std::cout << "kill man\n";
}
};
class Woman
{
private:
std::weak_ptr<Man> m_husband;
//std::shared_ptr<Man> m_husband;
public:
void setHusband(std::shared_ptr<Man> man)
{
m_husband = man;
}
~Woman()
{
std::cout << "kill woman\n";
}
};
int main(int argc, char** argv)
{
{
std::shared_ptr<Man> m(new Man());
std::shared_ptr<Woman> w(new Woman());
if (m && w)
{
m->setWife(w);
w->setHusband(m);
}
}
getchar();
return 0;
}
如果将Man和Woman对象内的私有成员换成shared_ptr;那么他们就互相引用了,当作用域结束后,他们的引用技术仍然不为0,造成内存泄露。
五 boost库scope_ptr
作用域指针
一个作用域指针独占一个动态分配的对象。 对应的类名为 boost::scoped_ptr,它的定义在 boost/scoped_ptr.hpp 中。 不像 std::auto_ptr,一个作用域指针不能传递它所包含的对象的所有权到另一个作用域指针。 一旦用一个地址来初始化,这个动态分配的对象将在析构阶段释放。
因为一个作用域指针只是简单保存和独占一个内存地址,所以 boost::scoped_ptr 的实现就要比 std::auto_ptr 简单。 在不需要所有权传递的时候应该优先使用 boost::scoped_ptr 。 在这些情况下,比起 std::auto_ptr 它是一个更好的选择,因为可以避免不经意间的所有权传递。
int main()
{
boost::scoped_ptr<int> a(new int);
*a = 1;
*a.get() = 2;
a.reset(new int);
//boost::scoped_ptr<int> b = a;//不允许
retur 0;
}
六 、boost库scoped_array
作用域数组,同scope_ptr,也是独享所有权的。
七 如何选择智能指针
(1)如果程序要使用多个指向同一个对象的指针,应选择 shared_ptr。这样的情况包括:
- 将指针作为参数或者函数的返回值进行传递的话,应该使用 shared_ptr;
- 两个对象都包含指向第三个对象的指针,此时应该使用 shared_ptr 来管理第三个对象;
- STL 容器包含指针。很多 STL 算法都支持复制和赋值操作,这些操作可用于 shared_ptr,但不能用于unique_ptr(编译器发出 warning)和 auto_ptr(行为不确定)。
(2)如果程序不需要多个指向同一个对象的指针,则可使用 unique_ptr。如果函数使用 new 分配内存,并返还指向该内存的指针,将其返回类型声明为 unique_ptr 是不错的选择。这样,所有权转让给接受返回值的 unique_ptr,而该智能指针将负责调用 delete。
(3)为了解决 shared_ptr 的循环引用问题,我们可以祭出 weak_ptr。
(4)在局部作用域(例如函数内部或类内部),且不需要将指针作为参数或返回值进行传递的情况下,如果对性能要求严格,使用 scoped_ptr 的开销较 shared_ptr 会小一些。
本文介绍了C++标准模板库STL中的四种智能指针:auto_ptr、unique_ptr、shared_ptr和weak_ptr的功能及用法,并对比了它们之间的区别。同时,文章还探讨了boost库中的scoped_ptr和scoped_array智能指针。

3742

被折叠的 条评论
为什么被折叠?



