智能指针模板类 | 支持情况 | 使用场景 |
---|---|---|
auto_ptr | C++98提供的,C++11已摒弃,但如果编译器不支持其它两种C++11提供的,则只能使用auto_ptr | |
unique_ptr | C++11提供 (如果编译器没提供,可使用Boost库的scoped_ptr) | 1. 不需要多个指向同一对象的指针 2. 支持下标法操作内部指针指向的元素 |
shared_ptr | C++11 提供(如果编译器没提供,可使用Boost库的shared_ptr) | 2. 多个指针指向同一个对象,支持复制和赋值操作 2.不支持下标法访问内部指针指向的元素> 注意:需要包含头文件 |
#include
#include
#include
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::shared_ptr<std::string> spStr = std::make_shared<std::string>("harry");
//! spStr用作一个条件判断,若spStr指向一个对象,则为true
if (spStr && spStr->empty())
{
*spStr = "hello world";
}
//! 也可以使用auto来简化变量的声明
auto p6 = std::make_shared<std::string>("hello");
std::cout << *p6 << std::endl;
使用make_shared分配内存失败会抛出异常,示例如下:
std::shared_ptr<std::string> spStr;
try
{
spThrd = std::make_shared<std::string>();
}
catch (std::bad_alloc& ex)
{
cat.info("[%s] 初始化失败,详情:%s", TESTITEM_NAME, ex.what());
return false;
}
catch (...)
{
cat.info("[%s] 初始化失败,未知错误", TESTITEM_NAME);
return false;
}
在类的构造函数初始化列表中初始化智能指针
class CStudent
{
public:
CStudent(int a)
:m_sp(std::make_shared<int>(a)){}
int Get() const {return *m_sp;}
private:
std::shared_ptr<int> m_sp;
};
void Test01()
{
CStudent s(3);
std::cout << s.Get();
}
//! 由于智能指针接受指针参数的构造函数是explicit,所以无法隐士转换。
shared_ptr<int> p1 = new int(1024); //< 错误,不支持隐士转换
shared_ptr<int> p2(new int(42)); //< 正确
auto p = std::make_shared<int>(5);
auto q(p);
if (!p.unique()) //< 判断是否唯一的用户(引用计数大于1),如果不是则重新分配新的拷贝
{
p.reset(new int(*p)); //< 重新分配新的拷贝,这个时候p所管理的内存就是自己独有的了,可以随意改变,不用担心影响其它用户
}
*p = 15;
unique_ptr<string> p1(new string("hello"));
//! 将所有权从p1(指向"hello")转移给p2
unique_ptr<string> p2(p1.release()); //< release将p1置为空
unique_ptr<string> p1(new string("world"));
unique_ptr<string> p2(new string("harry"));
//! 将所有权从p1转移给p2,release返回之p1内部所管理对象的指针,并将p1内部的指针置为空
p2.reset(p1.release()); //< reset释放了p2原来指向的内存
std::string str ("hello");
shared_ptr<std::string> pstr(&str); //< 不允许
// 因为pstr过期时, 析构会delete对象内部维护的std::string指针,而这里str是栈上的内存,不是堆空间的,这里将把delete运算符用于非堆内存,非法操作
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(); //< 正常
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也是使用所有权模型,与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();
std::unique_ptr<std::string> pa(new std::string("hello world"));
std::unique_ptr<std::string> pa1;
pa1 = pa; //< 这里在编译阶段就报错,但如果使用auto_ptr,则编译不会报错,
//< 使用pa内部维护的std::string指针时才会出错
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;
}
模板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对象
std::shared_ptr<int> ps(pu); //< 报错,编译器不允许右值为非临时变量
std::shared_ptr<int> puts(GetUni(3)); //< 正确,将临时的unique_ptr对象转为shared_ptr对象
return 0;
}
C++ Primer Plus(第6版)——16.2 智能指针模板类
将回调函数传递给 shared_ptr 的构造函数,该构造函数将从其析构函数中调用以进行删除
// 自定义删除器
void deleter(Sample * x)
{
std::cout << "Deleter function called" << std::endl;
delete[] x;
}
// 构造函数传递自定义删除器指针
std::shared_ptr<Sample> p1(new Sample[5], deleter);
class Deleter
{
public:
void operator() (Sample *x) {
std::cout << "Deleter function called" << std::endl;
delete[] x;
}
};
// 构造函数传递自定义删除器指针
std::shared_ptr<Sample> p2(new Sample[5], Deleter);
std::shared_ptr<Sample> p3(new Sample[5], [](Sample *x) {
std::cout << "Deleter function called" << std::endl;
delete[] x;
});
std::shared_ptr<Sample> p(new Sample[5], std::default_delete<Sample[]>());
需要使用decltype关键字
void Deleter(std::string* pstr)
{
delete pstr;
pstr = nullptr;
}
void Test01()
{
//!
std::unique_ptr<std::string, decltype(Deleter)*> up(new std::string("hello"), Deleter);
}
effective c++ 条款17 (第三版)
//! 假如如下函数
int priority();
void processWidget(std::shared_ptr<Widget> pw, int priority);
//! 调用
processWidget(std::shared_ptr<Widget>(new Widget), priority());
std::shared_ptr<Widget> pw(new Widget); //< 在单独语句内以只能指针存储newed所得的对象
processWidget(pw, priority()); //< 这个调用动作绝不至于造成泄漏
以上之所以可以,因为编译器对于“跨越语句的各项操作”没有重新排列的自由(只有在语句内它才拥有那个自由度)