自己的总体感觉
- make_shared构造智能指针
- use_count 可以参考引用的个数
- get返回被管理对象的指针
*解引用被管理对象->也是指向被管理对象的指针- 默认初始化创建的是空智能指针,可以想象为对象指针是空
- 内部结构有两个块, 一个指向被管理对象,一个是控制块,包含了引用计数、weak计数,构造释放的指针
- 如果第一次创建,两个块合并为一个,对象原地初始化
- weak的个数:
weak_ptr+ (number ofshared_ptr? 1 : 0) - 注意点 不要用get返回的指针创建智能指针
概述
std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptrobjects may own the same object. The object is destroyed and its memory deallocated when either of the following happens:
- the last remaining
shared_ptrowning the object is destroyed; - the last remaining
shared_ptrowning the object is assigned another pointer via operator= or reset().
智能指针管理的对象的释放的时机:
- 持有对象的最后一个智能指针释放
- 持有对象的最后一个智能指针通过reset赋值另一个对象。
The object is destroyed using delete-expression or a custom deleter that is supplied to shared_ptr during construction.
A shared_ptr can share ownership of an object while storing a pointer to another object. This feature can be used to point to member objects while owning the object they belong to. The stored pointer is the one accessed by get(), the dereference and the comparison operators. The managed pointer is the one passed to the deleter when use count reaches zero.
通过get 获取智能指针中存储的指针。
A shared_ptr may also own no objects, in which case it is called empty (an empty shared_ptr may have a non-null stored pointer if the aliasing constructor was used to create it).
All specializations of shared_ptr meet the requirements of CopyConstructible, CopyAssignable, and LessThanComparableand are contextually convertible to bool.
All member functions (including copy constructor and copy assignment) can be called by multiple threads on different shared_ptr objects without additional synchronization even if these objects are copies and share ownership of the same object. If multiple threads of execution access the same shared_ptr object without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur; the std::atomic<shared_ptr>can be used to prevent the data race.
多线程可以同时调用智能指针的方法, 但是同时访问智能指针会出现数据竞争的问题。
Implementation notes
In a typical implementation, shared_ptr holds only two pointers:
- the stored pointer (one returned by get());
- a pointer to control block.
智能指针拥有两个指针:指向对象的指针、控制快指针
The control block is a dynamically-allocated object that holds:
- either a pointer to the managed object or the managed object itself;
- the deleter (type-erased);
- the allocator (type-erased);
- the number of
shared_ptrs that own the managed object; - the number of
weak_ptrs that refer to the managed object.
控制快指针包含的部分:
- 被管理对象或被管理对象的指针
- 删除器
- allocator
- 引用计数
- weak_ptr的个数
When shared_ptr is created by calling std::make_shared or std::allocate_shared, the memory for both the control block and the managed object is created with a single allocation. The managed object is constructed in-place in a data member of the control block. When shared_ptr is created via one of the shared_ptr constructors, the managed object and the control block must be allocated separately. In this case, the control block stores a pointer to the managed object.
- make_shared 创建的内存包含对象和控制块, 是一体的。
- 通过智能指针创建的智能指针两个块时独立的,控制块存储的值对象的指针。
The pointer held by the shared_ptr directly is the one returned by get(), while the pointer/object held by the control block is the one that will be deleted when the number of shared owners reaches zero. These pointers are not necessarily equal.
The destructor of shared_ptr decrements the number of shared owners of the control block. If that counter reaches zero, the control block calls the destructor of the managed object. The control block does not deallocate itself until the std::weak_ptr counter reaches zero as well.
当引用计数为0的时候,被管理对象释放, 当weak_ptr的个数为0的时候,才释放控制块的内存
In existing implementations, the number of weak pointers is incremented ([1], [2]) if there is a shared pointer to the same control block.
weak 的个数 = weak_ptr + (number of shared_ptr ? 1 : 0)
核心的考虑是weak=0的时候,不判断User。
To satisfy thread safety requirements, the reference counters are typically incremented using an equivalent of std::atomic::fetch_add with std::memory_order_relaxed (decrementing requires stronger ordering to safely destroy the control block).
方法集
Modifiers
| 方法 | 描述 |
|---|---|
| reset | replaces the managed object (public member function) |
| swap | swaps the managed objects (public member function) |
Observers
| 方法 | 描述 |
|---|---|
| get | returns the stored pointer (public member function) |
| "[operatoroperator->](en.cppreference.com/w/cpp/memor… "cpp/memory/shared ptr/operator*)" | dereferences the stored pointer (public member function) |
| operator[](C++17) | provides indexed access to the stored array (public member function) |
| use_count | returns the number of shared_ptr objects referring to the same managed object (public member function) |
| unique(until C++20) | checks whether the managed object is managed only by the current shared_ptr object (public member function) |
| operator bool | checks if the stored pointer is not null (public member function) |
| owner_before | provides owner-based ordering of shared pointers (public member function) |
| owner_hash(C++26) | provides owner-based hashing of shared pointers (public member function) |
| owner_equal(C++26) | provides owner-based equal comparison of shared pointers (public member function) |
示例
#include <chrono>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
using namespace std::chrono_literals;
struct Base
{
Base() { std::cout << "Base::Base()\n"; }
// Note: non-virtual destructor is OK here
~Base() { std::cout << "Base::~Base()\n"; }
};
struct Derived : public Base
{
Derived() { std::cout << "Derived::Derived()\n"; }
~Derived() { std::cout << "Derived::~Derived()\n"; }
};
void print(auto rem, std::shared_ptr<Base> const& sp)
{
std::cout << rem << "\n\tget() = " << sp.get()
<< ", use_count() = " << sp.use_count() << '\n';
}
void thr(std::shared_ptr<Base> p)
{
std::this_thread::sleep_for(987ms);
std::shared_ptr<Base> lp = p; // thread-safe, even though the
// shared use_count is incremented
{
static std::mutex io_mutex;
std::lock_guard<std::mutex> lk(io_mutex);
print("Local pointer in a thread:", lp);
}
}
int main()
{
std::shared_ptr<Base> p = std::make_shared<Derived>();
print("Created a shared Derived (as a pointer to Base)", p);
std::thread t1{thr, p}, t2{thr, p}, t3{thr, p};
p.reset(); // release ownership from main
print("Shared ownership between 3 threads and released ownership from main:", p);
t1.join();
t2.join();
t3.join();
std::cout << "All threads completed, the last one deleted Derived.\n";
}
输出:
Base::Base()
Derived::Derived()
Created a shared Derived (as a pointer to Base)
get() = 0x118ac30, use_count() = 1
Shared ownership between 3 threads and released ownership from main:
get() = 0, use_count() = 0 0的原因下个小结解释
Local pointer in a thread:
get() = 0x118ac30, use_count() = 5
Local pointer in a thread:
get() = 0x118ac30, use_count() = 4
Local pointer in a thread:
get() = 0x118ac30, use_count() = 2
Derived::~Derived()
Base::~Base()
All threads completed, the last one deleted Derived.
reset的理解
#define COUT(str) std::cout << '\n' << str << '\n'
#define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n'
int main()
{
std::shared_ptr<std::string> p;
if (!p) {
COUT("1)默认空对象:");
DEMO(p.use_count());
}
p = std::make_shared<std::string>();
auto p2 = p;
COUT("2)p,p2指向string对象:");
DEMO(p.use_count());
DEMO(p2.use_count());
p.reset();
COUT("3)只有p2指向string对象:");
DEMO(p.use_count());
DEMO(p2.use_count());
p2.reset();
COUT("4)无智能指针指向string对象:");
DEMO(p.use_count());
DEMO(p2.use_count());
}
输出:
1)默认空对象:
p.use_count() = 0
2)p,p2指向string对象:
p.use_count() = 2
p2.use_count() = 2
3)只有p2指向string对象:
p.use_count() = 0
p2.use_count() = 1
4)无智能指针指向string对象:
p.use_count() = 0
p2.use_count() = 0
别名构造智能指针示例
#include <iostream>
#include <memory>
struct MyObj
{
MyObj() { std::cout << "MyObj constructed\n"; }
~MyObj() { std::cout << "MyObj destructed\n"; }
};
struct Container : std::enable_shared_from_this<Container> // note: public inheritance
{
std::shared_ptr<MyObj> memberObj;
void CreateMember() { memberObj = std::make_shared<MyObj>(); }
std::shared_ptr<MyObj> GetAsMyObj()
{
// Use an alias shared ptr for member
return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get());
}
~Container() {
std::cout << "Container destructed\n";
}
};
#define COUT(str) std::cout << '\n' << str << '\n'
#define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n'
int main()
{
COUT("Creating shared container");
std::shared_ptr<Container> cont = std::make_shared<Container>();
DEMO(cont.use_count()); //1
DEMO(cont->memberObj.use_count()); //0
//创建cont的memberObj成员, memberObj也是智能指针
COUT("Creating member");
cont->CreateMember();
DEMO(cont.use_count()); //1
DEMO(cont->memberObj.use_count()); //1
COUT("Creating another shared container");
//拷贝构造,逐成功copy, copy的是std::shared_ptr,所以cont的计数变为2,Container没有参与copy,所以memberObj还是1
// 自己做错了,认为memberObj是2
std::shared_ptr<Container> cont2 = cont;
DEMO(cont.use_count()); //2
DEMO(cont->memberObj.use_count()); //1
DEMO(cont2.use_count()); //2
DEMO(cont2->memberObj.use_count()); //1
COUT("GetAsMyObj");
//别名构造器, 构造的时候增加的引用计数是cont的,不是memberObj
//可以想象下: 增加的第一个参数的引用,那么控制块就指向了第一个参数的控制块cont, 管理对象执行的memberObj
std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj();
DEMO(myobj1.use_count()); //3
DEMO(cont.use_count()); //3
DEMO(cont->memberObj.use_count()); //1
DEMO(cont2.use_count()); //3
DEMO(cont2->memberObj.use_count()); //1
COUT("Copying alias obj");
// copy别名构造器,copy增加的引用计数也是cont的计数
std::shared_ptr<MyObj> myobj2 = myobj1;
DEMO(myobj1.use_count()); //4
DEMO(myobj2.use_count()); //4
DEMO(cont.use_count()); //4
DEMO(cont->memberObj.use_count()); //1
DEMO(cont2.use_count()); //4
DEMO(cont2->memberObj.use_count()); //1
COUT("Resetting cont2");
cont2.reset();
DEMO(myobj1.use_count()); //3
DEMO(myobj2.use_count()); //3
DEMO(cont.use_count()); //3
DEMO(cont->memberObj.use_count()); //1
DEMO(cont2.use_count()); //0
COUT("Resetting myobj2");
myobj2.reset();
DEMO(myobj1.use_count()); //2
DEMO(cont.use_count()); //2
DEMO(cont->memberObj.use_count()); //1
COUT("Resetting cont");
cont.reset();
DEMO(myobj1.use_count()); //1
DEMO(cont.use_count()); //0
COUT("Resetting myobj1");
myobj1.reset(); //这里执行了Container 和MyObj的析构
DEMO(myobj1.use_count()); //0
DEMO(cont.use_count()); //0
}
总结
- reset 减少了引用个数
- 哪个智能指针调用了reset, 哪个智能指针的use_count 变为了0,其他的智能指针的use_count不变。如p直接从2变为了0,但是p2还是1.
- reset的结果:内部的两个指针(控制块,对象)都为空
解引用
| T& operator*() const noexcept; | (1) | (since C++11) |
| T* operator->() const noexcept; |
1) The result of dereferencing the stored pointer, i.e., *get().
2) The stored pointer, i.e., get().
#include <iostream>
#include <memory>
struct Foo
{
Foo(int in) : a(in) {}
void print() const
{
std::cout << "a = " << a << '\n';
}
int a;
};
int main()
{
auto ptr = std::make_shared<Foo>(10);
ptr->print(); //a = 10
(*ptr).print(); //a = 10
}