C++智能指针
之前学习锁,用到了lock_guard,让我想起了自己智能指针部分还有所欠缺,故借机学习一波
智能指针:一个对于指针的封装,目的在于更好的管理内存,防止指针使用中的内存泄漏、二次释放等问题的产生。在C++的<memory>库中,有以下几种指针:auto_ptr
、auto_ptr_ref
、shared_ptr
、weak_ptr
、unique_ptr
和default_delete
。
1. auto_ptr
auto_ptr
在C++11已经被淘汰,其相应的功能可以通过unique_ptr
实现。在此不再赘述。
举个例子在此说明:
auto_ptr<string> p1(new string("auto"));
auto_ptr<string> p2;
p2 = p1; // p2接管了p1的使用权,之后再使用p1会报错
2. unique_ptr
std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes(处置、丢掉) of that object when the unique_ptr goes out of scope.
The object is disposed of, using the associated deleter when either of the following happens:
- the managing unique_ptr object is destroyed
- the managing unique_ptr object is assigned another pointer via operator= or reset().
The object is disposed of, using a potentially user-supplied deleter by calling get_deleter()(ptr). The default deleter uses the delete operator, which destroys the object and deallocates the memory.
A unique_ptr may alternatively own no object, in which case it is called empty.
There are two versions of std::unique_ptr:
- Manages a single object (e.g. allocated with new)
- Manages a dynamically-allocated array of objects (e.g. allocated with new[])
Deleter must be FunctionObject or lvalue reference to a FunctionObject or lvalue reference to function, callable with an argument of type unique_ptr<T, Deleter>::pointer
接口:(七个构造函数,五个类方法)
class unique_ptr {
public:
unique_ptr();
unique_ptr(nullptr_t Nptr);
explicit unique_ptr(pointer Ptr);
unique_ptr(pointer Ptr,
typename conditional<is_reference<Del>::value, Del,
typename add_reference<const Del>::type>::type Deleter);
unique_ptr(pointer Ptr,
typename remove_reference<Del>::type&& Deleter);
unique_ptr(unique_ptr&& Right);
template <class T2, Class Del2>
unique_ptr(unique_ptr<T2, Del2>&& Right);
unique_ptr(const unique_ptr& Right) = delete;
unique_ptr& operator=(const unique_ptr& Right) = delete;
};
//Specialization for arrays:
template <class T, class D>
class unique_ptr<T[], D> {
public:
typedef pointer;
typedef T element_type;
typedef D deleter_type;
constexpr unique_ptr() noexcept;
template <class U>
explicit unique_ptr(U p) noexcept;
template <class U>
unique_ptr(U p, see below d) noexcept;
template <class U>
unique_ptr(U p, see below d) noexcept;
unique_ptr(unique_ptr&& u) noexcept;
constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { } template <class U, class E>
unique_ptr(unique_ptr<U, E>&& u) noexcept;
~unique_ptr();
unique_ptr& operator=(unique_ptr&& u) noexcept;
template <class U, class E>
unique_ptr& operator=(unique_ptr<U, E>&& u) noexcept;
unique_ptr& operator=(nullptr_t) noexcept;
T& operator[](size_t i) const;
// function
// 1.returns a pointer to the managed object
pointer get() const noexcept;
// 2. returns the deleter that is used for destruction of the managed object
deleter_type& get_deleter() noexcept;
const deleter_type& get_deleter() const noexcept;
// checks if there is an associated managed object
explicit operator bool() const noexcept;
// 3.returns a pointer to the managed object and releases the ownership
pointer release() noexcept;
/*
eg:
#include <memory>
#include <iostream>
#include <cassert>
struct Foo {
Foo() { std::cout << "Foo\n"; }
~Foo() { std::cout << "~Foo\n"; }
};
int main()
{
std::cout << "Creating new Foo...\n";
std::unique_ptr<Foo> up(new Foo());
std::cout << "About to release Foo...\n";
Foo* fp = up.release();
assert (up.get() == nullptr);
assert (up == nullptr);
std::cout << "Foo is no longer owned by unique_ptr...\n";
delete fp;
}
output:
Creating new Foo...
Foo
About to release Foo...
Foo is no longer owned by unique_ptr...
~Foo
*/
// 4.replaces the managed object
void reset(pointer p = pointer()) noexcept;
void reset(nullptr_t = nullptr) noexcept;
template <class U>
void reset(U p) noexcept = delete;
/*
// eg:(reset使用)
#include <iostream>
#include <memory>
struct Foo { // object to manage
Foo() { std::cout << "Foo...\n"; }
~Foo() { std::cout << "~Foo...\n"; }
};
struct D { // deleter
void operator() (Foo* p) {
std::cout << "Calling delete for Foo object... \n";
delete p;
}
};
int main()
{
std::cout << "Creating new Foo...\n";
std::unique_ptr<Foo, D> up(new Foo(), D()); // up owns the Foo pointer (deleter D)
std::cout << "Replace owned Foo with a new Foo...\n";
up.reset(new Foo()); // calls deleter for the old one
std::cout << "Release and delete the owned Foo...\n";
up.reset(nullptr);
}
output:
Creating new Foo...
Foo...
Replace owned Foo with a new Foo...
Foo...
Calling delete for Foo object...
~Foo...
Release and delete the owned Foo...
Calling delete for Foo object...
~Foo...
*/
// 5. swaps the managed objects
void swap(unique_ptr& u) noexcept; // disable copy from lvalue unique_ptr(const unique_ptr&) = delete;
/*
eg:
#include <iostream>
#include <memory>
struct Foo {
Foo(int _val) : val(_val) { std::cout << "Foo...\n"; }
~Foo() { std::cout << "~Foo...\n"; }
int val;
};
int main()
{
std::unique_ptr<Foo> up1(new Foo(1));
std::unique_ptr<Foo> up2(new Foo(2));
up1.swap(up2);
std::cout << "up1->val:" << up1->val << std::endl;
std::cout << "up2->val:" << up2->val << std::endl;
}
output:
Foo...
Foo...
up1->val:2
up2->val:1
~Foo...
~Foo...
*/
unique_ptr& operator=(const unique_ptr&) = delete;
};
使用示例:
#include <cassert>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <memory>
#include <stdexcept>
// helper class for runtime polymorphism demo below
struct B
{
virtual ~B() = default;
virtual void bar() { std::cout << "B::bar\n"; }
};
struct D : B
{
D() { std::cout << "D::D\n"; }
~D() { std::cout << "D::~D\n"; }
void bar() override { std::cout << "D::bar\n"; }
};
// a function consuming a unique_ptr can take it by value or by rvalue reference
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
p->bar();
return p;
}
// helper function for the custom deleter demo below
void close_file(std::FILE* fp)
{
std::fclose(fp);
}
// unique_ptr-based linked list demo
struct List
{
struct Node
{
int data;
std::unique_ptr<Node> next;
};
std::unique_ptr<Node> head;
~List()
{
// destroy list nodes sequentially in a loop, the default destructor
// would have invoked its `next`'s destructor recursively, which would
// cause stack overflow for sufficiently large lists.
while (head)
head = std::move(head->next);
}
void push(int data)
{
head = std::unique_ptr<Node>(new Node{data, std::move(head)});
}
};
int main()
{
std::cout << "1) Unique ownership semantics demo\n";
{
// Create a (uniquely owned) resource
std::unique_ptr<D> p = std::make_unique<D>();
// Transfer ownership to `pass_through`,
// which in turn transfers ownership back through the return value
std::unique_ptr<D> q = pass_through(std::move(p));
// `p` is now in a moved-from 'empty' state, equal to `nullptr`
assert(!p);
}
std::cout << "\n" "2) Runtime polymorphism demo\n";
{
// Create a derived resource and point to it via base type
std::unique_ptr<B> p = std::make_unique<D>();
// Dynamic dispatch works as expected
p->bar();
}
std::cout << "\n" "3) Custom deleter demo\n";
std::ofstream("demo.txt") << 'x'; // prepare the file to read
{
using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>;
unique_file_t fp(std::fopen("demo.txt", "r"), &close_file);
if (fp)
std::cout << char(std::fgetc(fp.get())) << '\n';
} // `close_file()` called here (if `fp` is not null)
std::cout << "\n" "4) Custom lambda-expression deleter and exception safety demo\n";
try
{
std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr)
{
std::cout << "destroying from a custom deleter...\n";
delete ptr;
});
throw std::runtime_error(""); // `p` would leak here if it were instead a plain pointer
}
catch (const std::exception&) { std::cout << "Caught exception\n"; }
std::cout << "\n" "5) Array form of unique_ptr demo\n";
{
std::unique_ptr<D[]> p(new D[3]);
} // `D::~D()` is called 3 times
std::cout << "\n" "6) Linked list demo\n";
{
List wall;
for (int beer = 0; beer != 1'000'000; ++beer)
wall.push(beer);
std::cout << "1'000'000 bottles of beer on the wall...\n";
} // destroys all the beers
}
output:
1) Unique ownership semantics demo
D::D
D::bar
D::~D
2) Runtime polymorphism demo
D::D
D::bar
D::~D
3) Custom deleter demo
x
4) Custom lambda-expression deleter and exception safety demo
D::D
destroying from a custom deleter...
D::~D
Caught exception
5) Array form of unique_ptr demo
D::D
D::D
D::D
D::~D
D::~D
D::~D
6) Linked list demo
1'000'000 bottles of beer on the wall...
3. shared_ptr
std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of the following happens:
- the last remaining shared_ptr owning the object is destroyed;
- the last remaining shared_ptr owning the object is assigned another pointer via operator= or 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.
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 LessThanComparable and are contextually convertible to bool.
All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object. If multiple threads of execution access the same instance of shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of atomic functions(
std::atomic_...<std::shared_ptr>
) can be used to prevent the data race.
接口
- 1.
T* get() const noexcept;
A shared_ptr may share ownership of an object while storing a pointer to another object. get() returns the stored pointer, not the managed pointer.
#include <iostream>
#include <memory>
#include <string_view>
int main()
{
auto output = [](std::string_view msg, int const* pInt) {
std::cout << msg << *pInt << " in " << pInt << "\n";
};
int* pInt = new int(42);
std::shared_ptr<int> pShared = std::make_shared<int>(42);
output("Naked pointer ", pInt);
// output("Shared pointer ", pShared); // compiler error
output("Shared pointer with get() ", pShared.get());
delete pInt;
std::cout << "\nThe shared_ptr's aliasing constructor demo.\n";
struct Base1 { int i1{}; };
struct Base2 { int i2{}; };
struct Derived : Base1, Base2 { int i3{}; };
std::shared_ptr<Derived> p(new Derived());
std::shared_ptr<Base2> q(p, static_cast<Base2*>(p.get()));
std::cout << "q shares ownership with p, but points to Base2 subobject:\n"
<< "p.get(): " << p.get() << '\n'
<< "q.get(): " << q.get() << '\n'
<< "&(p->i1): " << &(p->i1) << '\n'
<< "&(p->i2): " << &(p->i2) << '\n'
<< "&(p->i3): " << &(p->i3) << '\n'
<< "&(q->i2): " << &(q->i2) << '\n';
}
/*
output:
Naked pointer 42 in 0xacac20
Shared pointer with get() 42 in 0xacac50
The shared_ptr's aliasing constructor demo.
q shares ownership with p, but points to Base2 subobject:
p.get(): 0xacac20
q.get(): 0xacac24
&(p->i1): 0xacac20
&(p->i2): 0xacac24
&(p->i3): 0xacac28
&(q->i2): 0xacac24
*/
- 2.
std::shared_ptr<T>::reset
Replaces the managed object with an object pointed to by ptr. Optional deleter d can be supplied, which is later used to destroy the new object when no shared_ptr objects own it. By default, delete expression is used as deleter. Proper delete expression corresponding to the supplied type is always selected, this is the reason why the function is implemented as template using a separate parameter Y.
If *this already owns an object and it is the last shared_ptr owning it, the object is destroyed through the owned deleter.
void reset() noexcept; //(1) (since C++11)
template< class Y >
void reset( Y* ptr ); //(2) (since C++11)
template< class Y, class Deleter >
void reset( Y* ptr, Deleter d ); //(3) (since C++11)
template< class Y, class Deleter, class Alloc >
void reset( Y* ptr, Deleter d, Alloc alloc ); //(4) (since C++11)
Example:
#include <memory>
#include <iostream>
struct Foo {
Foo(int n = 0) noexcept : bar(n) {
std::cout << "Foo::Foo(), bar = " << bar << " @ " << this << '\n';
}
~Foo() {
std::cout << "Foo::~Foo(), bar = " << bar << " @ " << this << "\n";
}
int getBar() const noexcept { return bar; }
private:
int bar;
};
int main()
{
std::cout << "1) unique ownership\n";
{
std::shared_ptr<Foo> sptr = std::make_shared<Foo>(100);
std::cout << "Foo::bar = " << sptr->getBar() << ", use_count() = "
<< sptr.use_count() << '\n';
// Reset the shared_ptr without handing it a fresh instance of Foo.
// The old instance will be destroyed after this call.
std::cout << "call sptr.reset()...\n";
sptr.reset(); // calls Foo's destructor here
std::cout << "After reset(): use_count() = " << sptr.use_count()
<< ", sptr = " << sptr << '\n';
} // No call to Foo's destructor, it was done earlier in reset().
std::cout << "\n2) unique ownership\n";
{
std::shared_ptr<Foo> sptr = std::make_shared<Foo>(200);
std::cout << "Foo::bar = " << sptr->getBar() << ", use_count() = "
<< sptr.use_count() << '\n';
// Reset the shared_ptr, hand it a fresh instance of Foo.
// The old instance will be destroyed after this call.
std::cout << "call sptr.reset()...\n";
sptr.reset(new Foo{222});
std::cout << "After reset(): use_count() = " << sptr.use_count()
<< ", sptr = " << sptr << "\nLeaving the scope...\n";
} // Calls Foo's destructor.
std::cout << "\n3) multiple ownership\n";
{
std::shared_ptr<Foo> sptr1 = std::make_shared<Foo>(300);
std::shared_ptr<Foo> sptr2 = sptr1;
std::shared_ptr<Foo> sptr3 = sptr2;
std::cout << "Foo::bar = " << sptr1->getBar() << ", use_count() = "
<< sptr1.use_count() << '\n';
// Reset the shared_ptr sptr1, hand it a fresh instance of Foo.
// The old instance will stay shared between sptr2 and sptr3.
std::cout << "call sptr1.reset()...\n";
sptr1.reset(new Foo{333});
std::cout << "After reset():\n"
<< "sptr1.use_count() = " << sptr1.use_count()
<< ", sptr1 @ " << sptr1 << '\n'
<< "sptr2.use_count() = " << sptr2.use_count()
<< ", sptr2 @ " << sptr2 << '\n'
<< "sptr3.use_count() = " << sptr3.use_count()
<< ", sptr3 @ " << sptr3 << '\n'
<< "Leaving the scope...\n";
} // Calls two destructors of: 1) Foo owned by sptr1,
// 2) Foo shared between sptr2/sptr3.
}
/*
output:
1) unique ownership
Foo::Foo(), bar = 100 @ 0x23c5040
Foo::bar = 100, use_count() = 1
call sptr.reset()...
Foo::~Foo(), bar = 100 @ 0x23c5040
After reset(): use_count() = 0, sptr = 0
2) unique ownership
Foo::Foo(), bar = 200 @ 0x23c5040
Foo::bar = 200, use_count() = 1
call sptr.reset()...
Foo::Foo(), bar = 222 @ 0x23c5050
Foo::~Foo(), bar = 200 @ 0x23c5040
After reset(): use_count() = 1, sptr = 0x23c5050
Leaving the scope...
Foo::~Foo(), bar = 222 @ 0x23c5050
3) multiple ownership
Foo::Foo(), bar = 300 @ 0x23c5080
Foo::bar = 300, use_count() = 3
call sptr1.reset()...
Foo::Foo(), bar = 333 @ 0x23c5050
After reset():
sptr1.use_count() = 1, sptr1 @ 0x23c5050
sptr2.use_count() = 2, sptr2 @ 0x23c5080
sptr3.use_count() = 2, sptr3 @ 0x23c5080
Leaving the scope...
Foo::~Foo(), bar = 300 @ 0x23c5080
Foo::~Foo(), bar = 333 @ 0x23c5050
*/
- 3.
void swap( shared_ptr& r ) noexcept;
Exchanges the stored pointer values and the ownerships of *this and r. Reference counts, if any, are not adjusted.
Exmaple:
#include <iostream>
#include <memory>
#include <string>
struct Foo {
Foo(int _val) : val(_val) { std::cout << "Foo...\n"; }
~Foo() { std::cout << "~Foo...\n"; }
std::string print() { return std::to_string(val); }
int val;
};
int main()
{
std::shared_ptr<Foo> p1 = std::make_shared<Foo>(100);
std::shared_ptr<Foo> p2 = std::make_shared<Foo>(200);
auto print = [&]() {
std::cout << " p1=" << (p1 ? p1->print() : "nullptr");
std::cout << " p2=" << (p2 ? p2->print() : "nullptr") << '\n';
};
print();
p1.swap(p2);
print();
p1.reset();
print();
p1.swap(p2);
print();
}
output:
Foo...
Foo...
p1=100 p2=200
p1=200 p2=100
~Foo...
p1=nullptr p2=100
p1=100 p2=nullptr
~Foo...
- 4.
long use_count() const noexcept;
Returns the number of different shared_ptr instances (this included) managing the current object. If there is no managed object, 0 is returned.
In multithreaded environment, the value returned by use_count is approximate (typical implementations use a memory_order_relaxed load)
引用计数增加的情况,拷贝一个shared_ptr,其所指对象的引用计数会递增,如:
- 用一个shared_ptr初始化另一个shared_ptr
- 用一个shared_ptr给另一个shared_ptr赋值
- 将shared_ptr作为参数传递给一个函数
- shared_ptr作为函数的返回值
引用计数减少的情况
1.给shared_ptr赋予一个新值
2.shared_ptr被销毁(如离开作用域)
Example:
#include <memory>
#include <iostream>
void fun(std::shared_ptr<int> sp)
{
std::cout << "in fun(): sp.use_count() == " << sp.use_count()
<< " (object @ " << sp << ")\n";
}
int main()
{
auto sp1 = std::make_shared<int>(5);
std::cout << "in main(): sp1.use_count() == " << sp1.use_count()
<< " (object @ " << sp1 << ")\n";
fun(sp1);
}
/*
output:
in main(): sp1.use_count() == 1 (object @ 0x20eec30)
in fun(): sp.use_count() == 2 (object @ 0x20eec30)
*/
- 5.
bool owner_before( const shared_ptr<Y>& other )
Checks whether this shared_ptr precedes other in implementation defined owner-based (as opposed to value-based) order. The order is such that two smart pointers compare equivalent only if they are both empty or if they both own the same object, even if the values of the pointers obtained by get() are different (e.g. because they point at different subobjects within the same object)
This ordering is used to make shared and weak pointers usable as keys in associative containers, typically through std::owner_less.
Example:
#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';
}
/*
Output:
p2 < p3 true
p3 < p2 false
p2.owner_before(p3) false
p3.owner_before(p2) false
w2.owner_before(w3) false
w3.owner_before(w2) false
*/
- 6.构造函数
直接上例子:
#include <memory>
#include <iostream>
struct Foo {
int id{0};
Foo(int i = 0) : id{i} { std::cout << "Foo::Foo(" << i << ")\n"; }
~Foo() { std::cout << "Foo::~Foo(), id=" << id << '\n'; }
};
struct D {
void operator()(Foo* p) const {
std::cout << "Call delete from function object. Foo::id=" << p->id << '\n';
delete p;
}
};
int main()
{
{
std::cout << "1) constructor with no managed object\n";
std::shared_ptr<Foo> sh1;
}
{
std::cout << "2) constructor with object\n";
std::shared_ptr<Foo> sh2(new Foo{10});
std::cout << "sh2.use_count(): " << sh2.use_count() << '\n';
std::shared_ptr<Foo> sh3(sh2);
std::cout << "sh2.use_count(): " << sh2.use_count() << '\n';
std::cout << "sh3.use_count(): " << sh3.use_count() << '\n';
}
{
std::cout << "3) constructor with object and deleter\n";
std::shared_ptr<Foo> sh4(new Foo{11}, D());
std::shared_ptr<Foo> sh5(new Foo{12}, [](auto p) {
std::cout << "Call delete from lambda... p->id=" << p->id << "\n";
delete p;
});
}
}
/*
Output:
1) constructor with no managed object
2) constructor with object
Foo::Foo(10)
sh2.use_count(): 1
sh2.use_count(): 2
sh3.use_count(): 2
Foo::~Foo(), id=10
3) constructor with object and deleter
Foo::Foo(11)
Foo::Foo(12)
Call delete from lambda... p->id=12
Foo::~Foo(), id=12
Call delete from function object. Foo::id=11
Foo::~Foo(), id=11
*/
4. weak_ptr
首先看如下的例子:
#include <iostream>
#include <memory>
#include <variant>
class Node {
char id;
// variant是C++中类型安全的union
std::variant<std::weak_ptr<Node>, std::shared_ptr<Node>> ptr;
public:
Node(char id) : id{id} {}
~Node() { std::cout << " '" << id << "' reclaimed\n"; }
/*...*/
void assign(std::weak_ptr<Node> p) { ptr = p; }
void assign(std::shared_ptr<Node> p) { ptr = p; }
};
enum class shared { all, some };
void test_cyclic_graph(const shared x) {
auto A = std::make_shared<Node>('A');
auto B = std::make_shared<Node>('B');
auto C = std::make_shared<Node>('C');
A->assign(B);
B->assign(C);
if (shared::all == x) { // 依赖环的最后一个指针是shared_ptr
C->assign(A);
std::cout << "All links are shared pointers";
} else { // 依赖环的最后一个指针是weak_ptr
C->assign(std::weak_ptr<Node>(A));
std::cout << "One link is a weak_ptr";
}
/*...*/
std::cout << "\nLeaving...\n";
}
int main() {
test_cyclic_graph(shared::some);
test_cyclic_graph(shared::all); // produces a memory leak
}
/*
output:
One link is a weak_ptr
Leaving...
'A' reclaimed
'B' reclaimed
'C' reclaimed // 所有的Node可以正常析构
All links are shared pointers
Leaving... // 由于所有shared_ptr的use_count均大于0,所以没有Node被析构
*/
std::weak_ptr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by std::shared_ptr. It must be converted to std::shared_ptr in order to access the referenced object.
#include <iostream>
#include <memory>
std::weak_ptr<int> gw;
void observe()
{
std::cout << "gw.use_count() == " << gw.use_count() << "; ";
// we have to make a copy of shared pointer before usage:
if (std::shared_ptr<int> spt = gw.lock()) {
std::cout << "*spt == " << *spt << '\n';
}
else {
std::cout << "gw is expired\n";
}
}
int main()
{
{
auto sp = std::make_shared<int>(42);
gw = sp;
observe();
}
observe();
}
/*
Output:
gw.use_count() == 1; *spt == 42
gw.use_count() == 0; gw is expired
*/
在上面的示例中,可以看到weak_ptr指向对象并不会导致use_count增加,当原本的shared_ptr失效后,虽然任然有weak_ptr指向该对象,但还是将该对象移除了。
for last
智能指针就先学这儿,以后想起点啥再补充。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?