C++11中智能指针分为三种:shared_ptr、unique_ptr、weak_ptr. (auto_ptr:discard)。
智能指针的主要功能是为了防止指针所指向的对象已经释放,出现内存泄露,野指针等问题。
一、智能指针
1.shared_ptr
1.1 实现方式:
shared_ptr中会有两个基本成员指针:一个用于存储我们用于初始化该class 的指针。One returned When calling member function,such as get().
另一个指针指向一个数据控制块(control block)。其作用主要是用于管理对象,数据控制块主要包括指向shared_ptr 所指对象的计数器(用于判断对象是否需要delete),指向weak_ptr 所指对象的计数器(shared_ptr中包含weak_ptr成员对象),deleter ,allocator(用于所指向对象的内存分配释放)等。
1.2 使用
- 初始化和 成员函数的使用
#include<iostream>
#include<memory>
using namespace std;
class Base
{
public:
int a;
Base(int k) :a(k) {}
~Base() {
cout << "Base is going to destructor" << endl;
}
};
int main()
{
// initial
Base* p = new Base(1);
//or auto sptr = make_shared<Base>(p);
shared_ptr<Base> sptr(p);
cout << "p =\t" << p << endl;
cout << "sptr=\t" << sptr << endl;
cout << "sptr.get()=\t" << sptr.get() << endl;
// member functions
// reset(), shared_ptr will call destructor function,when the number of count equal 0
cout << "test reset,destructor" << endl;
sptr.reset();
cout << "after reset, sptr=\t" << sptr << endl;
// swap()
Base* p2 = new Base(1);
shared_ptr<Base> sptr2(p2);
cout << "sptr2=\t" << sptr2 << endl;
sptr2.swap(sptr);
cout << "after swap with sptr,sptr2=\t" << sptr2 << endl;
cout << "after swap with sptr,p2=\t" << p2 << endl;
//use_count
shared_ptr<Base> sptr4(new Base(1));
cout << "the number of point to obj =\t" << sptr4.use_count() << endl;
sptr2 = sptr4;
cout << "after copy to sptr2,the number of point to obj of sptr4=\t" << sptr4.use_count() << endl;
cout << "after copy to sptr2,the number of point to obj of sptr2=\t" << sptr2.use_count() << endl;
return 0;
}
/* 结果:
p = 0097EBD8
sptr= 0097EBD8
sptr.get()= 0097EBD8
// member function
//reset
test reset,destructor
Base is going to destructor
after reset, sptr= 00000000
// swap
sptr2= 00985828
after swap with sptr,sptr2= 00000000
after swap with sptr,p2= 00985828
//use_count
the number of point to obj = 1
after copy to sptr2,the number of point to obj of sptr4= 2
after copy to sptr2,the number of point to obj of sptr2= 2
Base is going to destructor
Base is going to destructor
*/
- 多线程下 shared_ptr 共享指针计数器
#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(const char* 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";
}
/* result:
Base::Base()
Derived::Derived()
Created a shared Derived (as a pointer to Base)
get() = 0098E3F4, use_count() = 1
Shared ownership between 3 threads and released ownership from main:
get() = 00000000, use_count() = 0
Local pointer in a thread:
get() = 0098E3F4, use_count() = 6
Local pointer in a thread:
get() = 0098E3F4, use_count() = 4
Local pointer in a thread:
get() = 0098E3F4, use_count() = 2
Derived::~Derived()
Base::~Base()
All threads completed, the last one deleted Derived.
*/
2.unique_ptr
unique_ptr 指针用于管理一个对象,同一时刻,只能有一个这样的智能指针指向该对象。当调用 operator=,reset()时, unique_ptr会将先前的指针置为empty。
#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;
}
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);
}
}
/*
1) Unique ownership semantics demo
D::D
D::bar
D::~D
*/
3.weak_ptr
weak_ptr 是基于 shared_ptr 实现的指针类型,但weak_ptr 的赋值并不会引起shared_ptr指针计数器的变化。可以用于解决成员指针shared_ptr 链式指向对象,构成一个环时,在当前函数栈执行完成进行析构时,造成的这两个指针计数器不会下降为0,资源永远不会释放的局面。
// 有A,B,C三个Node对象,对象的成员指针构成一个环状,在使用weak_ptr和shared_ptr时,对象会有不同的释放内存结果。
#include <iostream>
#include <memory>
class Node
{
char id;
std::weak_ptr<Node> wptr;
std::shared_ptr<Node> sptr;
public:
Node(char id) : id{ id } {}
~Node() { std::cout << " '" << id << "' reclaimed\n"; }
/*...*/
void assign(std::weak_ptr<Node> p) { wptr = p; }
void assign(std::shared_ptr<Node> p) { sptr = 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)
{
C->assign(A);
std::cout << "All links are shared pointers";
}
else
{
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
}
/*
//当有一根指针是weak_ptr指针的时候,三个对象依次被析构,且因为weak_ptr是指向A的,所以在析构时从A开始,依次按照内部成员指针的连接顺序析构。
One link is a weak_ptr
Leaving...
'A' reclaimed
'B' reclaimed
'C' reclaimed
//当所有的指针都是shared_ptr指针的时候,没有任何析构函数被调用,出现内存泄露。
All links are shared pointers
Leaving...
*/