part4 智能指针
1 裸指针问题
(1) 没指明 指向 单个对象
还是数组
(2) 没指明 是否应该销毁
所指内容
(3) delete形式必须对: delete / delete []; 否则, 结果未定义
(4) 很难确保销毁
准确地沿着代码中的每一条路径
(包括由 异常
导致的路径)执行一次
缺少路径 -> 资源泄漏; 多次销毁 -> 未定义行为
(5) 通常无法判断
指针是否悬空
, 即 是否指向原对象
智能指针 几乎可以做原始指针所能做的一切
,但错误使用的机会更少
2 std::auto_ptr 问题
std::auto_ptr copy 动作
(copy ctor 和 copy assignment)只在
源和目的 auto_ptr都没用 const 修饰
时具有 move 语义
; 否则, 编译报错
原因: copy ctor 和 copy assignment 的形参都是 non-const 引用
auto_ptr(auto_ptr& __a) throw()
: _M_ptr(__a.release() ) {}
auto_ptr&
operator=(auto_ptr& __a);
=> auto_ptr 无法用于 容器
C++98/11 容器 push_back 形参类型是 const 左值引用/ 多了 右值引用
, push_back 底层
对 引用的对象进行 copy 语义
操作 => auto_ptr 作实参调 容器 push_back
时, 编译报错
push_back(const value_type& __x);
void push_back (value_type&& val); // C++11 新增
3 std::unique_ptr 完成 std::auto_ptr 所做的一切, 以及更多
避免 auto_ptr 会扭曲 copy 对象的含义的问题
item18 std::unique_ptr 用于 独享所有权的资源管理
(1) 不支持 copy 语义, 只支持 move
语义: 移动后, 源 unique_ptr 为空
(2) 编译器不支持
把 裸指针(如 new 返回值) 直接赋值给 unique_ptr
解决: 用 reset
移动构造/移动赋值
unique_ptr( unique_ptr&& u ) noexcept;
template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept;
unique_ptr& operator=( unique_ptr&& r ) noexcept;
template< class U, class E >
unique_ptr&
operator=( unique_ptr<U, E>&& r ) noexcept;
unique_ptr&
operator=( std::nullptr_t ) noexcept;
(3) unique_ptr 作 STL 容器的元素
时, 不能直接传递, 因为不能拷贝和赋值 -> 解决: 用 std::move() 传递
unique_ptr<int> up(new int(20) );
vector<unique_ptr<int> > vec;
vec.push_back(std::move(up) );
(4) unique_ptr 作 STL 容器的元素时, 不能作为类的成员变量
class A
{
private:
vector<unique_ptr<int> > vec; //错误
};
remember
(1) unique_ptr 小、快、只支持移动、独享所有权
(2) 资源销毁: 默认 用 delete
; 第2模板形参可指定 用户 Deleter
(3) std::unique_ptr 可容易、高效地转换为 std::shared_ptr
1 常见用法
(1) 在类层次中, 作 对象的 Factory Method 返回类型
caller 独占 工厂返回的资源
class Investment { … };
class Stock: public Investment
{ … };
class Bond: public Investment
{ … };
class RealEstate: public Investment
{ … };
template<typename... Ts> // return std::unique_ptr
std::unique_ptr<Investment> // to an object created
makeInvestment(Ts&&... params); // from the given args
{
// ...
auto pInvestment = // pInvestment is of type std::unique_ptr<Investment>
makeInvestment( arguments );
// …
} // destroy *pInvestment
(2) 所有权 迁移
场景
路径: 工厂
返回资源
-> 被移动到容器
中 -> 移动到对象的成员 data
中 -> 对象销毁 -> 成员销毁过程中: 先销毁原资源
所有权链 中断
(因 异常
等原因)时, 也能保证 原资源被销毁: 只要保证管理资源的 unique_ptr 的 Dtor 能被触发
2 第2模板参数可用于指定 Deleter
默认下, 销毁将通过 delete
进行
unique_ptr 还可以指定 Deleter
(函数/函数对象/lambda)
(1) Deleter 不含状态
时优先选 lambda/函数对象
auto delInvmt = [](Investment* pInvestment) // custom deleter, a lambda expression
{
makeLogEntry(pInvestment);
delete pInvestment;
};
通过基类指针 delete 子类对象
=> 基类 Dtor 必须为 virtual
class Investment {
public:
…
virtual ~Investment(); // base dtor must be virtual
…
};
template<typename... Ts>
std::unique_ptr<Investment, decltype(delInvmt)> // return type
makeInvestment(Ts&&... params)
{
// (1) 创建 空 unique_ptr
std::unique_ptr<Investment, decltype(delInvmt)>
pInv(nullptr, delInvmt);
// (2) 指向合适对象
if ( /* a Stock object should be created */ )
{
pInv.reset(new Stock(std::forward<Ts>(params)...));
}
else if ( /* a Bond object should be created */ )
{
pInv.reset(new Bond(std::forward<Ts>(params)...));
}
else if ( /* a RealEstate object should be created */ )
{
pInv.reset(new RealEstate(std::forward<Ts>(params)...));
}
// (3) 返回
return pInv;
}
(2) Deleter 含状态
时优先选 函数(指针)
void delInvmt2(Investment* pInvestment) // custom deleter as function
{
makeLogEntry(pInvestment);
delete pInvestment;
}
template<typename... Ts> // return type has
std::unique_ptr<Investment, // size of Investment*
void (*)(Investment*)> // plus at least size
makeInvestment(Ts&&... params); // of function pointer!
3 管理 单个对象 / 数组
unique_ptr<T> / unique_ptr<T[]>
unique_ptr 管理数组通常没有实践价值
std::array / std::vector / std::string 通常总是比原始数组
更好
1个例外
: 类 C 的 API
, 返回一个裸指针指向堆数组
4 unique_ptr 最吸引人的特性: 容易、高效地转换
为 shared_ptr
工厂总是返回 unique_ptr(比 shared_ptr 高效
), client 需要时转换
成 shared_ptr
std::shared_ptr<Investment> sp = // converts std::unique_ptr to std::shared_ptr
makeInvestment( arguments );
item19 std::shared_ptr
用于 共享所有权的资源管理
1 Deleter 不再作 第2模板形参, 而是作 Ctor 的第2参数
auto loggingDel = [](Widget *pw) // custom deleter
{
makeLogEntry(pw);
delete pw;
};
std::unique_ptr< Widget, // deleter type is
decltype(loggingDel)> // part of ptr type
upw(new Widget, loggingDel);
std::shared_ptr<Widget> // deleter type is not
spw(new Widget, loggingDel); // part of ptr type
auto customDeleter1 = [](Widget *pw) { … }; // custom deleters,
auto customDeleter2 = [](Widget *pw) { … }; // each with a different type
std::shared_ptr<Widget> pw1(new Widget, customDeleter1);
std::shared_ptr<Widget> pw2(new Widget, customDeleter2);
std::vector<std::shared_ptr<Widget>> vpw{ pw1, pw2 };
2 错误用法
(1) 用 裸指针 赋值给 不同的 shared_ptr: 不能 直接赋值
原因: 第2个之后的 shared_ptr 会建立新的 Control Block
=> 1份资源 对应 多份 RC(引用计数)
auto pw = new Widget; // pw is raw ptr
…
std::shared_ptr<Widget> spw1(pw, loggingDel); // create control block for *pw
…
std::shared_ptr<Widget> spw2(pw, loggingDel); // create 2nd control block for *pw!
解决: 必须通过第1个 shared_ptr 中转
std::shared_ptr<Widget> spw1(new Widget, // direct use of new
loggingDel);
std::shared_ptr<Widget> spw2(spw1); // spw2 uses same
// control block as spw1
(2) 对象自身指针 this, 直接用于 赋给/构造
shared_ptr -> 与 (1) 的问题本质相同
std::vector<std::shared_ptr<Widget>> processedWidgets;
class Widget
{
public:
…
void process();
…
};
void Widget::process()
{
… // process the Widget
processedWidgets.emplace_back(this); // add it to list of
} // processed Widgets;
// this is wrong!
解决: 类继承自 std::enable_shared_from_this
+ 工厂函数 + this 换成 shared_from_this()
CRTP
class Widget: public std::enable_shared_from_this<Widget>
{
public:
// factory function that perfect-forwards args to a private ctor
template<typename... Ts>
static std::shared_ptr<Widget>
create(Ts&&... params);
…
void process(); // as before
…
private:
… // ctors
};
void Widget::process()
{
// as before, process the Widget
…
// add std::shared_ptr to current object to processedWidgets
processedWidgets.emplace_back(shared_from_this());
}
CRTP
奇怪的循环模板模式: 子类 作 基类模板实参
(1) 静态多态
: 基类 non-virtual 接口函数
中 static_cast 强转 this
为基类指针
, 然后调子类的实现函数
实例化基类
时, 编译器已经知道
了子类的 实现函数声明
template <class T>
struct Base
{
void interface()
{
// ...
static_cast<T*>(this)->implementation();
// ...
}
static void static_func()
{
// ...
T::static_sub_func();
// ...
}
};
struct Derived : Base<Derived>
{
void implementation();
static void static_sub_func();
};
(2) 子类实例创建和析构独立的计数
item20 std::weak_ptr
用于可能悬挂
的 类似 std::shared_ptr 的指针
(1) weak_ptr -> shared_ptr
[1] 构造: 作 arg 构造
[2] 赋值: lock()
成员函数
(2) shared_ptr -> weak_ptr
[1] 构造: 作 arg 构造
[2] 赋值: 作 arg 赋值
1 监控
相应的 std::shared_ptr 所管理的资源 是否 被销毁
, 不影响 资源的 RC
std::weak_ptr 悬挂 == 过期( expired )
<=> 资源 被销毁
auto spw = // after spw is constructed,
std::make_shared<Widget>(); // the pointed-to Widget's ref count (RC) is 1
…
std::weak_ptr<Widget> wpw(spw); // wpw points to same Widget as spw.
// RC remains 1
…
spw = nullptr; // RC goes to 0, Widget is destroyed.
// wpw now dangles
if (wpw.expired() ) … // if wpw doesn't point to an object…
// true
std::shared_ptr<Widget> spw1
= wpw.lock(); // if wpw's expired, spw1 is null
auto spw2 = wpw.lock(); // same as above, but uses auto
由 weak_ptr 构造 shared_ptr
std::shared_ptr<Widget> spw3(wpw); // if wpw's expired,
// throw std::bad_weak_ptr
2 weak_ptr + shared_ptr + unique_ptr 融合
的例子: 基于 cache(缓存) 的 快速加载
std::unique_ptr<const Widget>
loadWidget(WidgetID id);
std::shared_ptr<const Widget>
fastLoadWidget(WidgetID id)
{
// (1) 无序 map: 资源 ID, 资源 的 weak_ptr(监控资源是否被销毁)
static std::unordered_map<WidgetID,
std::weak_ptr<const Widget> > cacheControlBlock;
// (2) 取出资源的管理指针: 取出 指定 ID 的 weak_ptr, 赋值给 shared_ptr
auto objPtr = cacheControlBlock[id].lock(); // objPtr is std::shared_ptr
// to cached object (or null
// if object's not in cache)
// (3) 若 shared_ptr 为空(即 资源不在 cache), 加载资源(用工厂函数), 用缓存控制块管理起来
if (!objPtr) // if not in cache
{
objPtr = loadWidget(id); // load it
cacheControlBlock[id] = objPtr; // cache it
}
// (4) 返回 资源的管理指针
return objPtr;
}
3 指针悬挂 & 循环引用
A/B 内部都有 指针指向对方
, 且外部都被 shared_ptr 管理
(1) 1方(如 A)内部应该放 shared_ptr
(2) 另1方(如 B)内部
放
[1] 裸指针
-> 问题: A 销毁后, B 的指针悬挂, 而 B 检测不到指针悬挂
[2] shared_ptr
-> 问题: 循环引用
+ A/B 的 RC = 2
=> A/B lifetime 结束
时, 由于 A/B 的 RC 只能减为 1
=> A/B 均内存泄漏
[3] weak_ptr
-> A 的 RC = 1, B 的 RC = 2 => A/B lifetime 结束
时, A/B 的 RC 减为 0/1, A 销毁
, B能检测到自己的指针悬挂
=> B 的 RC 减为 0, B 销毁
item21 std::make_unique 和 std::make_shared 优先于直接 new
template<typename T, typename... Ts>
std::unique_ptr<T>
make_unique(Ts&&... params)
{
return std::unique_ptr<T>(
new T(std::forward<Ts>(params)...) );
}
item22 使用 Pimp
时, 在实现文件中定义 特殊成员函数
remember
(1) Pimp 通过 减少编译依赖性 来 减少构建时间
(2) unique_ptr 用于 pImpl 指针, 必须要在所属类的 头文件中声明/源文件中 unique_ptr 所指类定义之后定义
特殊成员函数(Dtor + Move Ctor/Assignment 或 Copy Ctor/Assignment
)
(3) shared_ptr 没有像 unique_ptr 的要求
// "widget.h"
class Widget
{
public:
Widget();
…
private:
std::string name;
std::vector<double> data;
Gadget g1, g2, g3; // Gadget is some user-defined type
};
1 pimp 所指 struct
作 pimp 所属类的成员
, 在 pimp 所属类的头文件中 只声明不定义
// "widget.h"
class Widget
{
public:
Widget();
~Widget(); // dtor is needed—see below
…
private:
struct Impl; // declare implementation struct
Impl *pImpl; // and pointer to it
};
pimp 所指类 在 pimp 所属类的源文件中 定义
// "widget.cpp"
#include "widget.h" // in impl. file "widget.cpp"
#include "gadget.h"
#include <string>
#include <vector>
struct Widget::Impl // definition of Widget::Impl
{
std::string name; // with data members formerly
std::vector<double> data; // in Widget
Gadget g1, g2, g3;
};
Widget::Widget() // allocate data members for
: pImpl(new Impl) // this Widget object
{}
Widget::~Widget() // destroy data members for
{ delete pImpl; } // this object
2 pimp 从 裸指针 换成 unique_ptr
// in "widget.h"
class Widget
{
public:
Widget();
…
private:
struct Impl;
std::unique_ptr<Impl> pImpl; // use smart pointer instead of raw pointer
};
// "widget.cpp"
#include "widget.h"
#include "gadget.h"
#include <string>
#include <vector>
struct Widget::Impl // as before
{
std::string name;
std::vector<double> data;
Gadget g1, g2, g3;
};
Widget::Widget() // per Item 21, create
: pImpl(std::make_unique<Impl>()) // std::unique_ptr
{} // via std::make_unique
3 client 编译报错
#include "widget.h"
Widget w; // error!
原因: 对不完整的类型 sizeof 或 delete
编译器 自动生成的 Dtor
销毁 unique_ptr
时
[1] 先用 static_assert
保证 unique_ptr 所指对象 Widget::Impl
是完整的(Dtor 能看到 Widget::Impl 的定义)
默认 Dtor 在 头文件中 inline
=> 看不到 源文件中 Widget::Impl 的定义
=> static_assert fail
[2] 再调 unique_ptr 的 Dtor
: delete unique_ptr 管理的 cache
解决: 头文件中显式声明 Dtor, 源文件中 Widget::Impl 定义之后 定义 Dtor
// "widget.h"
class Widget {
public:
Widget();
~Widget(); // declaration only
…
private: // as before
struct Impl;
std::unique_ptr<Impl> pImpl;
};
// "widget.cpp"
#include "widget.h"
#include "gadget.h"
#include <string>
#include <vector>
struct Widget::Impl // as before
{
std::string name;
std::vector<double> data;
Gadget g1, g2, g3;
};
Widget::Widget() // per Item 21, create
: pImpl(std::make_unique<Impl>()) // std::unique_ptr
{}
Widget::~Widget() // ~Widget definition
{}
Widget::~Widget() = default; // same effect as above
4 Pimp 自然地支持 移到语义
, 但 声明 Dtor 会阻止编译器自动生成 move Ctor/Assignment
-> 解决: 显式声明
move Ctor/Assignment
// "widget.h"
class Widget {
public:
Widget();
~Widget();
Widget(Widget&& rhs); // declarations
Widget& operator=(Widget&& rhs); // only
…
private: // as before
struct Impl;
std::unique_ptr<Impl> pImpl;
};
// "widget.cpp"
#include <string>
…
struct Widget::Impl { … };
Widget::Widget() // as before
: pImpl(std::make_unique<Impl>() )
{}
Widget::~Widget() = default; // as before
Widget::Widget(Widget&& rhs) = default; // defini-
Widget& Widget::operator=(Widget&& rhs) = default; // tions
<=>
Widget::Widget(Widget&& rhs)
: pImpl(rhs.pImpl) {}
Widget&
Widget::operator=(Widget&& rhs)
{
pImpl = rhs.pImpl; // unique_ptr 的 移动赋值
}
5 copy 语义
class Widget { // still in "widget.h"
public:
… // other funcs, as before
Widget(const Widget& rhs); // declarations
Widget& operator=(const Widget& rhs); // only
private: // as before
struct Impl;
std::unique_ptr<Impl> pImpl;
};
#include "widget.h" // as before,
… // in "widget.cpp"
struct Widget::Impl { … }; // as before
Widget::~Widget() = default; // other funcs, as before
Widget::Widget(const Widget& rhs) // copy ctor
: pImpl(std::make_unique<Impl>(*rhs.pImpl) )
{}
Widget&
Widget::operator=(const Widget& rhs) // copy operator=
{
*pImpl = *rhs.pImpl;
return *this;
}
6 unique_ptr 换成 shared_ptr
std::shared_ptr Deleter 类型 不是 智能指针类型的一部分
=>
[1] 编译器生成 更大的 运行时数据结构
和 更慢的 运行时代码
[2] 所属类 调用其 Dtor/Copy ctor(Assignment)
中 不要求 shared_ptr 所指 对象是完整(能被看到)的
class Widget { // in "widget.h"
public:
Widget();
… // no declarations for dtor
// or move operations
private:
struct Impl;
std::shared_ptr<Impl> pImpl; // std::shared_ptr
}; // instead of std::unique_ptr
Widget w1;
auto w2(std::move(w1)); // move-construct w2
w1 = std::move(w2); // move-assign w1