智能指针
使用智能指针
auto_ptr,unique_ptr,shared_ptr都定义了类似指针的对象,可以将new获得(直接或间接)的地址赋给这种对象。当智能指针对象过期时,其析构函数将使用delete 来释放内存。
创建使用智能指针,包含头文件
throw()意味着构造函数不会引发异常,与auto_ptr一样,throw也被抛弃。
auto_ptr pd (new double);
#include <iostream>
#include<string>
#include<memory>
using namespace std;
class Report {
private:
string str;
public:
Report(const string s) :str(s) { cout << str << " created \n"; }
~Report()
{
cout << str << " delete \n";
}
void comment() const { cout << str << endl; }
};
int main()
{
std::cout << "Hello World!\n";
{
auto_ptr<Report> ps(new Report("use auto"));
ps->comment();
}
{
shared_ptr<Report> ps(new Report("use share"));
ps->comment();
}
{
unique_ptr<Report> ps(new Report("use unique"));
ps->comment();
}
}
- 所有智能指针类都有一个explicit构造函数,该构造函数将指针作为参数。因此不需要自动将指针转为智能指针对象。
shared_ptr pd;
double* p_reg = new double;
pd = p_reg; // not allowed
pd = shared_ptr(p_reg); //ok
shared_ptr ps = p_reg; // not allowed
shared_ptr ps (p_reg); //ok - 智能指针与常规指针类似,可以 ps-> 或者*ps
- 智能指针应该避免
string s(“safsgag”);
shared_ptr ps(&s);
ps过期时,程序将把delete运算符用于非堆的内存
智能指针注意事项
实际上有四种,还有weak_ptr,为何摒弃auto_prt;
auto_ptr ps(new string(“sssss”));
auto_ptr pd ;
ps = pd;
如果ps和pd是常规指针,则两个指针将指向同一个string对象。这样程序将试图删除同一个对象两次,一次是ps过期,一次是pd过期。避免这种问题方法有很多。
1.定义赋值运算,使之执行深度赋值。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本
2.建立所有权概念,对于特定的对象,只有一个智能指针能拥有它。这样只有拥有对象的智能指针的析构函数会删除该对象。然后让赋值操作转让所有权,这就是用于auto和unique的策略,但后者更严格。
3.创建更高的指针,跟踪引用特定对象的智能指针数。这称为引用计数。赋值时,计数+1,对象过期时-1。仅当最后一个对象过期时,才调用delete。(shared_ptr)
4. 不适用auto_ptr的例子
int main()
{
auto_ptr<string> a[4] =
{
auto_ptr<string>(new string("088")),
auto_ptr<string>(new string("134")),
auto_ptr<string>(new string("5667")),
auto_ptr<string>(new string("145125")),
};
auto_ptr<string> pa;
pa = a[2]; //若使用unique_ptr 编译不通过,此处报错
//此处将所有权从a【2】给pa,a【2】不再引用该字符串。在 auto_ptr放弃对象所有权后,便可能使用它来访问该对象,此时*a【2】为空指针
cout << "________________________\n";
for (auto p : a)
cout << *p << endl; //打印5667异常退出 ,若改用shared_ptr则不会;
cout << "the is " << *pa << endl;
}
unique_ptr优于auto_ptr
auto_ptr<string>p1(new string("088"));
auto_ptr<string>p2;
p2 = p1; //p2接管string对象所有权后,p1的所有权将被剥夺。可以防止析构同一个对象两次,但是此时p1指向无效的数据,使用p1会出现错误
unique_ptr<string>p1(new string("088"));
unique_ptr<string>p2;
p2 = p1; //此时编译器将报错
- 函数中可以
unique_ptr<string> demo(const char* s)
{
unique_ptr<string> tmp(new string(s));
return tmp;
}
int main()
{
unique_ptr<string> ps;
ps = demo("okl");
//允许demo返回临时的,然后ps接管了,ps拥有对象的所有权,临时的将很快销毁,没有机会使用无效的数据
}
总之,程序试图将一个unique_ptr赋给另一个时,如果源unique_ptr是一个临时右值,编译器允许这样;但源unique_ptr存在很长一段时间,则编译器禁止。
unique_ptr p1 (new string(“hello”));
unique_ptr ps;
ps = p1; // no 此时p1悬挂,危险。非要这样 ,不要使用智能指针
unique_ptr p2;
p2 = unique_ptr (new string(“hello”)); //ok 调用构造,临时变量很快被销毁
2. c++有一个标准库函数std::move(),能够将一个unique_ptr赋给另一个。
unique_ptr<string> demo(const char* s)
{
unique_ptr<string> tmp(new string(s));
return tmp;
}
int main()
{
unique_ptr<string> ps1,ps2;
ps1 = demo("wttkk");
ps2 = move(ps1);
ps1 = demo("wt");
cout << *ps1 << endl;
cout << *ps2 << endl;
}
- unique_ptr还有另一个优点,它有一个可用于数组的变体。
auto_ptr只能和new一起使用
unique_ptr<double []>pad(new double(5));
注意:智能指针只适用于用new或new【】分配的内存。
选择智能指针
1.如果程序使用多个指向同一对象的指针,应选择shared_ptr.这样的情况包括:有一个指针数组,并使用一些辅助指针来标识特定的元素,如最大和最小;两个对象包含都指向第三个元素的指针;STL容器包含指针。若编译器没有shared_ptr.使用Boost库提供的shared_ptr.
2.如果程序不需要多个指向同一个对象的指针,则可以使用 unique_ptr。如果函数使用new分配内存,并返回指向该内存的地址,将其声明为unique_ptr是不错的选择。这样所有权将转让给接受返回值的 unique_ptr,而该智能指针将负责调用delete。可将 unique_ptr存到容器中,只要不调用将一个 unique_ptr复制或赋给另一个的方法或算法(如sort())。