目录
头文件<memory>
1、std::unique_ptr
声明:
template<class T,class Deleter = std::default_delete<T>> class unique_ptr; |
template <class T,class Deleter> class unique_ptr<T[], Deleter>; |
std::unique_ptr是通过指针占有并管理另一对象,并在unique_ptr离开作用域时释放该对象的智能指针。
在下列两者之一发生时用关联的删除器释放对象:
- 销毁了管理的 unique_ptr 对象
- 通过 operator= 或 reset() 赋值另一指针给管理的 unique_ptr 对象。
通过调用 get_deleter()(ptr) ,用潜在为用户提供的删除器释放对象。默认删除器用 delete 运算符,它销毁对象并解分配内存。
unique_ptr 亦可以不占有对象,该情况下称它为空 (empty)。
std::unique_ptr 有两个版本:
1) 管理单个对象(例如以 new 分配)
2) 管理动态分配的对象数组(例如以 new[] 分配)
类满足可移动构造 (MoveConstructible) 和可移动赋值 (MoveAssignable) 的要求,但不满足可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 的要求。
注意点:
只有非 const 的 unique_ptr
能转移被管理对象的所有权给另一 unique_ptr
。若对象的生存期为 const std::unique_ptr 所管理,则它被限定在创建指针的作用域中。
std::unique_ptr
常用于管理对象的生存期,包含:
- 通过正常退出和经由异常退出两者上的受保证删除,提供异常安全,给处理拥有动态生存期的对象的类和函数
- 传递独占的拥有动态生存期的对象的所有权到函数
- 从函数获得独占的拥有动态生存期对象的所有权
- 移动容器的元素类型,例如保有指向动态分配对象的指针的 std::vector (例如,若想要多态行为)
#include <iostream>
#include <vector>
#include <memory>
#include <cstdio>
#include <fstream>
#include <cassert>
#include <functional>
struct B {
virtual void bar() { std::cout << "B::bar\n"; }
virtual ~B() = default;
};
struct D : B
{
D() { std::cout << "D::D\n"; }
~D() { std::cout << "D::~D\n"; }
void bar() override { std::cout << "D::bar\n"; }
};
// 消费 unique_ptr 的函数能以值或以右值引用接收它
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
p->bar();
return p;
}
void close_file(std::FILE* fp) { std::fclose(fp); }
int main()
{
std::cout << "unique ownership semantics demo\n";
{
auto p = std::make_unique<D>(); // p 是占有 D 的 unique_ptr
auto q = pass_through(std::move(p));
assert(!p); // 现在 p 不占有任何内容并保有空指针
q->bar(); // 而 q 占有 D 对象
} // ~D 调用于此
std::cout << "Runtime polymorphism demo\n";
{
std::unique_ptr<B> p = std::make_unique<D>(); // p 是占有 D 的 unique_ptr
// 作为指向基类的指针
p->bar(); // 虚派发
std::vector<std::unique_ptr<B>> v; // unique_ptr 能存储于容器
v.push_back(std::make_unique<D>());
v.push_back(std::move(p));
v.emplace_back(new D);
for(auto& p: v) p->bar(); // 虚派发
} // ~D called 3 times
std::cout << "Custom deleter demo\n";
std::ofstream("demo.txt") << 'x'; // 准备要读的文件
{
std::unique_ptr<std::FILE, void (*)(std::FILE*) > fp(std::fopen("demo.txt", "r"),
close_file);
if(fp) // fopen 可以打开失败;该情况下 fp 保有空指针
std::cout << (char)std::fgetc(fp.get()) << '\n';
} // fclose() 调用于此,但仅若 FILE* 不是空指针
// (即 fopen 成功)
std::cout << "Custom lambda-expression deleter demo\n";
{
std::unique_ptr<D, std::function<void(D*)>> p(new D, [](D* ptr)
{
std::cout << "destroying from a custom deleter...\n";
delete ptr;
}); // p 占有 D
p->bar();
} // 调用上述 lambda 并销毁 D
std::cout << "Array form of unique_ptr demo\n";
{
std::unique_ptr<D[]> p{new D[3]};
} // 调用 ~D 3 次
}
2、std::shared_ptr
std::shared_ptr 是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象。下列情况之一出现时销毁对象并释放其内存:
- 最后剩下的占有对象的 shared_ptr 被销毁;
- 最后剩下的占有对象的 shared_ptr 被通过 operator= 或 reset() 赋值为另一指针。
修改器:
reset | 替换所管理的对象 // sptr.reset(new Foo); 指针sptr指向新的对象(new Foo),原对象解引用 |
swap | 交换所管理的对象 // sptr.reset(r) ; 交换 sptr 与 r 的内容 |
观察器:
get | 返回存储的指针 |
use_count | 返回 shared_ptr 所指对象的引用计数 |
unique | 检查所管理对象是否仅由当前 shared_ptr 的实例管理 |
owner_before | //提供基于拥有者的共享指针排序,x.owner_before(q) 如果q被认为与x不同,并根据所有权以严格的弱顺序排在它之前,则返回true。 |
先来看一个owner_before的例子
// shared_ptr::owner_before
#include <iostream>
#include <memory>
int main () {
int * p = new int (10);
std::shared_ptr<int> a (new int (20));
std::shared_ptr<int> aa (a);
std::shared_ptr<int> b (a,p); //a与b指向同样的内容,但是b不包含p
std::shared_ptr<int> c (new int (20));
std::cout << "comparing a and b...\n" << std::boolalpha;
std::cout << "a=aa: " << (a==aa) << std::endl; //a与aa指向同样的内容,相等
std::cout << "a<b: " << (a<b) << std::endl;
std::cout << "a=b: " << (a==b) << std::endl;
std::cout << "a>b: " << (a>b) << std::endl;
std::cout << "a>c: " << (a>c) << std::endl;
std::cout << "a.owner_before(aa): " << a.owner_before(aa) << std::endl;
std::cout << "aa.owner_before(a): " << aa.owner_before(a) << std::endl;
std::cout << "a.owner_before(b): " << a.owner_before(b) << std::endl;
std::cout << "b.owner_before(a): " << b.owner_before(a) << std::endl;
std::cout << "a.owner_before(c): " << a.owner_before(c) << std::endl; //a和c完全不同,且a在c之前
std::cout << "c.owner_before(a): " << c.owner_before(a) << std::endl;
delete p;
return 0;
}
运行结果:
再看一个关于owner_before的例子
#include <iostream>
#include <memory>
struct Foo {
int n1;
int n2;
Foo(int a, int b) : n1(a), n2(b) {}
};
int main()
{
auto p1 = std::make_shared<Foo>(1, 2);
std::shared_ptr<int> p2(p1, &p1->n1);
std::shared_ptr<int> p3(p1, &p1->n2);
std::cout << std::boolalpha
<< "p2 < p3 " << (p2 < p3) << '\n'
<< "p3 < p2 " << (p3 < p2) << '\n'
<< "p2.owner_before(p3) " << p2.owner_before(p3) << '\n'
<< "p3.owner_before(p2) " << p3.owner_before(p2) << '\n';
std::weak_ptr<int> w2(p2);
std::weak_ptr<int> w3(p3);
std::cout
// << "w2 < w3 " << (w2 < w3) << '\n' // won't compile
// << "w3 < w2 " << (w3 < w2) << '\n' // won't compile
<< "w2.owner_before(w3) " << w2.owner_before(w3) << '\n'
<< "w3.owner_before(w2) " << w3.owner_before(w2) << '\n';
}
最后来看一个shared_ptr的综合应用的实例
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
struct Base
{
Base() { std::cout << " Base::Base()\n"; }
// 注意:此处非虚析构函数 OK
~Base() { std::cout << " Base::~Base()\n"; }
};
struct Derived: public Base
{
Derived() { std::cout << " Derived::Derived()\n"; }
~Derived() { std::cout << " Derived::~Derived()\n"; }
};
void thr(std::shared_ptr<Base> p)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::shared_ptr<Base> lp = p; // 线程安全,虽然自增共享的 use_count
{
static std::mutex io_mutex;
std::lock_guard<std::mutex> lk(io_mutex);
std::cout << "local pointer in a thread:\n"
<< " lp.get() = " << lp.get()
<< ", lp.use_count() = " << lp.use_count() << '\n';
}
}
int main()
{
std::shared_ptr<Base> p = std::make_shared<Derived>();
std::cout << "Created a shared Derived (as a pointer to Base)\n"
<< " p.get() = " << p.get()
<< ", p.use_count() = " << p.use_count() << '\n';
std::thread t1(thr, p), t2(thr, p), t3(thr, p);
p.reset(); // 从 main 释放所有权
std::cout << "Shared ownership between 3 threads and released\n"
<< "ownership from main:\n"
<< " p.get() = " << p.get()
<< ", p.use_count() = " << p.use_count() << '\n';
t1.join(); t2.join(); t3.join();
std::cout << "All threads completed, the last one deleted Derived\n";
}
3、std::weak_ptr
std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性(“弱”)引用。在访问所引用的对象前必须先转换为 std::shared_ptr。
std::weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。
std::weak_ptr 的另一用法是打断 std::shared_ptr 所管理的对象组成的环状引用。若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。能令环中的指针之一为弱指针以避免此情况。
修改器:
reset | 释放被管理对象的所有权 |
swap | 交换被管理对象 |
观察器:
use_count | 返回管理该对象的shared_ptr对象数量 |
expired | 检查被引用的对象是否已删除 |
lock | 创建被引用的对象的shared_ptr |
owner_before | 提供弱智真的基于拥有者顺序,参考shared_ptr |
#include <iostream>
#include <memory>
std::weak_ptr<int> gw;
void f()
{
if (auto spt = gw.lock()) { // 使用之前必须复制到 shared_ptr
std::cout << *spt << "\n";
}
else {
std::cout << "gw is expired\n";
}
}
int main()
{
{
auto sp = std::make_shared<int>(42);
gw = sp;
f();
}
f();
}
运行结果: