模板种类
智能指针模板类 | 支持情况 | 使用场景 |
---|---|---|
auto_ptr | C++98提供的,C++11已摒弃,但如果编译器不支持其它两种C++11提供的,则只能使用auto_ptr | |
unique_ptr | C++11提供 (如果编译器没提供,可使用Boost库的scoped_ptr) | 不需要多个指向同一对象的指针 |
shared_ptr | C++11 提供(如果编译器没提供,可使用Boost库的shared_ptr) | 多个指针指向同一个对象,支持复制和赋值操作 |
智能指针使用示例
#include <memory>
#include <string>
#include <iostream>
class CReport
{
private:
std::string m_str;
public:
CReport(const std::string &str)
:m_str(str)
{
std::cout << "Object created!!!\n";
}
~CReport()
{
std::cout << "Object deleted!!!\n";
}
void Show() const
{
std::cout << m_str << "\n";
}
};
void Test01()
{
//! 都是模板类,本质就是模板类中维护一个T*指针,通过析构函数来delete T*指针
std::auto_ptr<CReport> pa(new CReport("using auto_ptr"));
pa->Show();
std::shared_ptr<CReport> ps(new CReport("using shared_ptr"));
ps->Show();
std::unique_ptr<CReport> pu(new CReport("using unique_ptr"));
pu->Show();
}
有关智能指针的注意事项
- 三种智能指针都应避免
std::string str ("hello");
shared_ptr<std::string> pstr(&str); //< 不允许
// 因为pstr过期时, 析构会delete对象内部维护的std::string指针,而这里str是栈上的内存,不是堆空间的,这里将把delete运算符用于非堆内存,非法操作
- 三种智能指针对赋值语句的策略
- auto_ptr
std::auto_ptr<std::string> pa(new std::string("hello world"));
std::auto_ptr<std::string> pa1;
pa1 = pa; //< 赋值的时候,移交了所有权给pa1
std::cout <<* pa.get(); //< 异常,因为pa交出了所有权,所以pa维护的std::string指针为空,导致以打印的时候会出错
std:: cout << *pa1.get(); //< 正常
- shared_ptr
std::shared_ptr<std::string> pa(new std::string("hello world")); //< 引用计数+1
std::shared_ptr<std::string> pa1; //< 引用计数+1
pa1 = pa; //< 内部维护的std::string指针指向同一个堆空间
std::cout <<*pa.get() << std::endl; //< 正常,没有所有权的概念,采用引用计数的方式,
//< 只有引用计数降为0的时候,析构才delete所维护的std::string指针
std:: cout << *pa1.get(); //< 正常
- unique_ptr
//! unique_ptr也是使用所有权模型,与auto_ptr一样,但unique_ptr赋值会在编译阶段就报错
std::unique_ptr<std::string> pa(new std::string("hello world"));
std::unique_ptr<std::string> pa1;
pa1 = pa; //< 这里在编译阶段就报错
std::cout <<*pa.get() << std::endl;
std:: cout << *pa1.get();
unique_ptr优于auto_ptr
- 编译器阶段错误比潜在的程序崩溃更安全
std::unique_ptr<std::string> pa(new std::string("hello world"));
std::unique_ptr<std::string> pa1;
pa1 = pa; //< 这里在编译阶段就报错,但如果使用auto_ptr,则编译不会报错,
//< 使用pa内部维护的std::string指针时才会出错
- 允许赋值的情况
程序试图将一个unique_ptr赋给另一个时,如果源unique_ptr是个临时右值,编译器允许这样做;但如果源unique_ptr将存在一段时间,编译器将禁止这样做。如下代码示例:
std::unique_ptr<std::string> pu(new std::string("hello world"));
std::unique_ptr<std::string> pu1;
// pu1 = pu; //< 编译器禁止这么做,因为pu长期存在,可能会存在使pu中的std::string对象的情况
std::unique_ptr<std::string> pu3;
pu3 = std::unique_ptr<std::string>(new std::string("hello world")); //< 允许,因为右边生成的临时对象,再把std::string对象的所有权移交给pu3后,会很快被销毁,没有机会使用它来访问无效的数据
//! 函数的返回值,也是临时右值,可以这么使用,当把临时右值赋值给接收者变量std::unique_ptr后,
//! 转移了所有权,临时右值会被销毁
std::unique_ptr<std::string> Get()
{
std::unique_ptr<std::string> pu(new std::string("hello"));
return pu;
}
- 可用于数组的变体
使用new分配内存——可以使用auto_ptr、shared_ptr和unique_ptr
使用new[]分配内存——可以使用unique_ptr
unique_ptr转为shared_ptr
模板shared_ptr包含一个显示构造函数,可将右值unique_ptr转换为shared_ptr,share_ptr将接管原来归unique_ptr所有的对象
std::unique_ptr<int> GetUni(int n)
{
return std::unique_ptr<int>(new int(n));
}
int main()
{
std::unique_ptr<int> pu(GetUni(3)); //< 正确,pu构造函数内为临时的unique_ptr<int>对象
std::shared_ptr<int> ps(pu); //< 报错,编译器不允许右值为非临时变量
std::shared_ptr<int> puts(GetUni(3)); //< 正确,将临时的unique_ptr<int>对象转为shared_ptr<int>对象
return 0;
}
参考书籍
C++ Primer Plus(第6版)——16.2 智能指针模板类