Effective Modern C++

【Effective Modern C++(4)】智能指针

2018-12-06  本文已影响60人  downdemo

18 对专属所有权的资源管理使用std::unique_ptr

class Investment { … }; // 投资
class Stock: public Investment { … }; // 股票
class Bond: public Investment { … }; // 债券
class RealEstate: public Investment { … }; // 不动产
template<typename... Ts>
std::unique_ptr<Investment>
makeInvestment(Ts&&... params);
{
    …
    auto pInvestment = // pInvestment is of type
    makeInvestment( arguments ); // std::unique_ptr<Investment>
    …
}
auto delInvmt =
    [](Investment* pInvestment) // 删除器需要接受Investment*类型实参
    {
        makeLogEntry(pInvestment);
        delete pInvestment;
    };

template<typename... Ts>
std::unique_ptr<Investment, decltype(delInvmt)> // 改进的返回类型
makeInvestment(Ts&&... params)
{
    std::unique_ptr<Investment, decltype(delInvmt)>  // 待返回指针
        pInv(nullptr, delInvmt);
    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)...));
    }
    return pInv;
}
class Investment {
public:
    …
    virtual ~Investment();
    …
};
template<typename... Ts>
auto makeInvestment(Ts&&... params) // C++14
{
    auto delInvmt = // 删除器现在位于makeInvestment内部
        [](Investment* pInvestment)
        {
            makeLogEntry(pInvestment);
            pInvestment;
        };

    // 其他不变
    std::unique_ptr<Investment, decltype(delInvmt)> pInv(nullptr, delInvmt);
    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)...));
    }
    return pInv;
}
auto delInvmt1 = // 使用无捕获lambda作为删除器
    [](Investment* pInvestment)
    {
        makeLogEntry(pInvestment);
        delete pInvestment;
    };

template<typename... Ts>
std::unique_ptr<Investment, decltype(delInvmt1)> // 返回值尺寸与Investment*相同
makeInvestment(Ts&&... args);


void delInvmt2(Investment* pInvestment) // 使用函数作为删除器
{
    makeLogEntry(pInvestment);
    delete pInvestment;
}

template<typename... Ts>
std::unique_ptr<Investment, // 返回类型尺寸至少为Investment*加函数指针的尺寸
void (*)(Investment*)>
makeInvestment(Ts&&... params);
auto f()
{
    unique_ptr<int> x = make_unique<int>(42);
    return x;
}
shared_ptr<int> p = f(); // OK
shared_ptr<int> q = make_unique<int>(42); // OK
unique_ptr<int> p = make_unique<int>(42);
shared_ptr<int> q = p; // 错误

19 对共享所有权的资源管理使用std::shared_ptr

sp1 = sp2; // sp1指向sp2所指的对象,sp1原先指向的对象计数递减,sp2递增
auto loggingDel = [](Widget *pw)
{
    makeLogEntry(pw);
    delete pw;
};

std::unique_ptr<Widget, decltype(loggingDel)> // 删除器是类型的一部分
upw(new Widget, loggingDel);

std::shared_ptr<Widget> // 删除器不是类型的一部分
spw(new Widget, loggingDel);
auto customDeleter1 = [](Widget *pw) { … }; 
auto customDeleter2 = [](Widget *pw) { … };

std::shared_ptr<Widget> pw1(new Widget, customDeleter1);
std::shared_ptr<Widget> pw2(new Widget, customDeleter2);
// pw1和pw2虽然有不同的删除器,但有相同类型,方便放入容器
std::vector<std::shared_ptr<Widget>> vpw{ pw1, pw2 };
std::shared_ptr<T>相关内存
int main()
{
    {
        int* i = new int(42);
        shared_ptr<int> p(i);
        shared_ptr<int> q(i);
    } // error
}
std::shared_ptr<int> p(new int(42));
std::vector<std::shared_ptr<A>> v;
class A {
public:
    …
    void f();
    …
};
void A::f()
{
    … // 处理A对象本身
    v.emplace_back(this); // 把原始指针加入元素类型为std::shared_ptr的容器
}
class A: public std::enable_shared_from_this<A> {
public:
    …
    void f();
    …
};
void A::f()
{
    …
    v.emplace_back(shared_from_this()); // 添加指向当前对象的std::shared_ptr到容器
}
class A : public std::enable_shared_from_this<A> {
public:
    template<typename... Ts>
    static std::shared_ptr<A> create(Ts&&... params);
    …
    void f();
    …
private:
    … // 构造函数
};
shared_ptr<int[]> p(new int[4] {1,2,3,4});
std::cout << p[1];

20 使用std::weak_ptr替代可能空悬的std::shared_ptr

auto p = std::make_shared<A>(); // p构造完成后,指向A的引用计数为1
…
std::weak_ptr<A> q(p); // 引用计数仍为1
…
p = nullptr; // 引用计数为0,A被析构,q空悬
if (q.expired()) …
std::shared_ptr<A> p2 = q.lock(); // q失效则p2为nullptr
auto p3 = q.lock(); // 可以直接使用auto
std::shared_ptr<A> p3(q);

std::weak_ptr的用处

std::unique_ptr<const Widget> loadWidget(WidgetID id);
std::shared_ptr<const Widget> fastLoadWidget(WidgetID id)
{
    static std::unordered_map<WidgetID, std::weak_ptr<const Widget>> cache;
    auto objPtr = cache[id].lock(); // objPtr is std::shared_ptr
    if (!objPtr) { // if not in cache,
        objPtr = loadWidget(id); // load it
        cache[id] = objPtr; // cache it
    }
    return objPtr;
}
#include <iostream>
#include <memory>

using namespace std;
class B;
class A {
public:
    A() { cout << "A\n"; }
    ~A() { cout << "-A\n"; }
    shared_ptr<B> b;
};

class B{
public:
    B() { cout << "B\n"; }
    ~B() { cout << "-B\n"; }
    shared_ptr<A> a;
};

int main()
{
    {
        shared_ptr<A> a(new A);
        a->b = shared_ptr<B>(new B);
        a->b->a = a;
    }
    cout << "OK";
    cin.get();
}

// output
A
B
OK
#include <iostream>
#include <memory>

using namespace std;
class B;
class A {
public:
    A() { cout << "A\n"; }
    ~A() { cout << "-A\n"; }
    shared_ptr<B> b;
};

class B{
public:
    B() { cout << "B\n"; }
    ~B() { cout << "-B\n"; }
    weak_ptr<A> a;
};

int main()
{
    {
        shared_ptr<A> a(new A);
        a->b = shared_ptr<B>(new B);
        a->b->a = a;
    }
    cout << "OK";
    cin.get();
}

// output
A
B
-A
-B
OK

21 使用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)...));
}
auto upw1(std::make_unique<A>()); // with make func
std::unique_ptr<A> upw2(new A); // without make func
auto spw1(std::make_shared<A>()); // with make func
std::shared_ptr<A> spw2(new A); // without make func
void f(std::shared_ptr<A> p, int n);
int fac() { return 1; }
f(std::shared_ptr<A>(new A), // 潜在的资源泄露
    fac());
f(std::make_shared<A>(), // 不会发生潜在的资源泄露
    fac()
auto del = [](A* pa) { … };
std::unique_ptr<A, decltype(del)> p(new A, del);
std::shared_ptr<A> q(new A, del);
auto upv = std::make_unique<std::vector<int>>(10, 20);
auto spv = std::make_shared<std::vector<int>>(10, 20);
// create std::initializer_list
auto initList = { 10, 20 };
// create std::vector using std::initializer_list ctor
auto spv = std::make_shared<std::vector<int>>(initList);
class ReallyBigType { … };
auto pBigObj = std::make_shared<ReallyBigType>(); // 创建大尺寸对象
… // 创建指向该对象的多个std::shared_ptr和std::weak_ptr并做一些操作
… // 最后一个std::shared_ptr被析构,但std::weak_ptr仍存在
… // 此时,大尺寸对象占用内存仍未被回收
… // 最后一个std::weak_ptr被析构,control block和对象占用的同一内存块被释放
class ReallyBigType { … };
std::shared_ptr<ReallyBigType> pBigObj(new ReallyBigType); // 创建大尺寸对象
… // 创建指向该对象的多个std::shared_ptr和std::weak_ptr并做一些操作
… // 最后一个std::shared_ptr被析构,但std::weak_ptr仍存在,不过大对象占用内存被回收
… // 此时,仅control block内存处于分配而未回收状态
… // 最后一个std::weak_ptr被析构,control block的内存块被释放
void f(std::shared_ptr<A> p, int n);
void del(A *pa);
f(std::shared_ptr<A>(new A, del), fac()); // 潜在的资源泄漏
std::shared_ptr<A> p(new A, del);
f(p, fac()); // 不存在资源泄漏,但不是最优化
f(std::shared_ptr<A>(new A, del), fac()); // 非异常安全调用:实参是右值
f(p, fac()); // 异常安全调用:实参是左值
f(std::move(p), fac()); // 同时保证效率和异常安全

22 使用pimpl手法时,将特殊成员函数定义在实现文件中

// file "widget.h"
class Widget {
public:
    Widget();
…
private:
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3; // Gadget是某个自定义类型
};
// file "widget.h"
class Widget {
public:
    Widget();
    ~Widget(); // 必须有析构函数
    …
private:
    struct Impl; // 声明实现的struct
    Impl *pImpl; // 指向实现类的指针
};
// file "widget.cpp"
#include "widget.h"
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl { // Widget::Impl的实现
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3;
};
Widget::Widget() // 为Widget对象的数据成员分配内存
: pImpl(new Impl)
{}

Widget::~Widget() // 回收数据成员的内存
{ delete pImpl; }
// file "widget.h"
#include <memory>

class Widget {
public:
    Widget();
    …
private:
    struct Impl;
    std::unique_ptr<Impl> pImpl;
};

// file "widget.cpp"
#include "widget.h"
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl {
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3;
};

Widget::Widget()
: pImpl(std::make_unique<Impl>())
{}
// file "main.cpp"
#include "widget.h"

Widget w;

// 诊断信息
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\memory(2082): error C2338: can't delete an incomplete type
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\memory(2084): warning C4150: 删除指向不完整“Widget::Impl”类型的指针;没有调用析构函数
<memory>中的源码
// file "widget.h"
#include <memory>

class Widget {
public:
    Widget();
    ~Widget(); // 只声明
    …
private:
    struct Impl;
    std::unique_ptr<Impl> pImpl;
};

// file "widget.cpp"
#include "widget.h"
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl {
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3;
};

Widget::Widget()
: pImpl(std::make_unique<Impl>())
{}

Widget::~Widget() // 析构函数的实现位于Widget::Impl的实现之后
{}

// file "main.cpp"
#include "widget.h"

Widget w; // OK
// file "widget.cpp"
Widget::~Widget() = default;
// file "widget.h"
class Widget {
public:
    Widget();
    ~Widget();
    Widget(Widget&& rhs) = default; // right idea,
    Widget& operator=(Widget&& rhs) = default; // wrong code!
    …
private:
    struct Impl;
    std::unique_ptr<Impl> pImpl;
};
// file "widget.h"
#include <memory>

class Widget {
public:
    Widget();
    ~Widget(); // 只声明
    Widget(Widget&& rhs);
    Widget& operator=(Widget&& rhs);
    …
private:
    struct Impl;
    std::unique_ptr<Impl> pImpl;
};

// file "widget.cpp"
#include "widget.h"
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl {
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3;
};

Widget::Widget()
: pImpl(std::make_unique<Impl>())
{}

Widget::~Widget() = default;
Widget::Widget(Widget&& rhs) = default;
Widget& Widget::operator=(Widget&& rhs) = default;
// file "widget.h"
#include <memory>

class Widget {
public:
    Widget();
    ~Widget(); // 只声明
    Widget(Widget&& rhs);
    Widget& operator=(Widget&& rhs);
    Widget(const Widget& rhs);
    Widget& operator=(const Widget& rhs);
    …
private:
    struct Impl;
    std::unique_ptr<Impl> pImpl;
};

// file "widget.cpp"
#include "widget.h"
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl {
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3;
};

Widget::Widget()
: pImpl(std::make_unique<Impl>())
{}

Widget::~Widget() = default;
Widget::Widget(Widget&& rhs) = default;
Widget& Widget::operator=(Widget&& rhs) = default;

Widget::Widget(const Widget& rhs)
: pImpl(std::make_unique<Impl>(*rhs.pImpl))
{}

Widget& Widget::operator=(const Widget& rhs) // copy operator=
{
    *pImpl = *rhs.pImpl;
    return *this;
}
// file "widget.h"
#include <memory>

class Widget {
public:
    Widget();
    …
private:
    struct Impl;
    std::shared_ptr<Impl> pImpl;
};

// file "widget.cpp"
#include "widget.h"
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl {
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3;
};

Widget::Widget()
: pImpl(std::make_shared<Impl>())
{}

// file "main.cpp"
Widget w1; // 默认构造w1
auto w2(std::move(w1)); // 移动构造w2
w1 = std::move(w2); // 移动赋值w1
上一篇 下一篇

猜你喜欢

热点阅读