std::unique_ptr
是通过指针占有并管理另一对象,并在 unique_ptr
离开作用域时释放该对象的智能指针。
std::unique_ptr
常用于管理对象的生存期,包含:
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 pass_through(std::unique_ptr 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(); // p 是占有 D 的 unique_ptr
auto q = pass_through(std::move(p));
assert(!p); // 现在 p 不占有任何内容并保有空指针
q->bar(); // 而 q 占有 D 对象
} // ~D 调用于此
唯一性指针删除了拷贝构造和赋值重载,只保留了移动构造和移动赋值:
auto q = pass_through(std::move(p));
此代码也可运行通过:
std::unique_ptr
res(std::move(p));
int main()
{
std::cout << "Custom lambda-expression deleter demo\n";
{
std::unique_ptr> p(new D, [](D* ptr)
{
std::cout << "destroying from a custom deleter...\n";
delete ptr;
}); // p 占有 D
p->bar();
} // 调用上述 lambda 并销毁 D
}
执行结果:
Custom lambda-expression deleter demo
D::D
D::bar
destroying from a custom deleter...
D::~DE:\ccc\helloc\Debug\helloc.exe (进程 9996)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
std::shared_ptr
是通过指针保持对象共享所有权的智能指针。多个 shared_ptr
对象可占有同一对象。下列情况之一出现时销毁对象并解分配其内存:
shared_ptr
被销毁;shared_ptr
被通过 operator= 或 reset() 赋值为另一指针。shared_ptr
能在存储指向一个对象的指针时共享另一对象的所有权。此特性能用于在占有其所属对象时,指向成员对象。存储的指针为 get() 、解引用及比较运算符所访问。被管理指针是在 use_count 抵达零时传递给删除器者。
代码:
struct D;
using namespace std;
struct B {
std::shared_ptrmb;//观察res1
void bar() { std::cout << "B::bar\n"; }
B() { std::cout << "B::bar\n"; }
B(std::shared_ptrp) :mb(p){ std::cout << "B::create\n"; }
~B() { std::cout << "B::~D\n"; }
};
struct D
{
D() { std::cout << "D::CREATE\n"; }
std::shared_ptrmd;//观察res2
D(std::shared_ptrp) :md(p){ std::cout << "D::D\n"; }
~D() { std::cout << "D::~D\n"; }
void bar() { std::cout << "D::bar\n"; }
};
int main()
{
shared_ptrres1(new D);
shared_ptrres2 = std::make_shared(res1);
res1->md = res2;
}
问题造成的结果:
无法释放被管理的对象。
问题的原因:
1、 此两个智能指针释放时,uses减少1为1,并不会释放ptr资源.
问题的解决办法:
使用弱引用智能指针。
1.uses和weaks初始都为1。
2.在相互赋值智能指针成员对象时发生:
根据函数入栈顺序,先析构res1,uses由1减少1为0,weaks由2减少为1;析构指向的资源,并将ptr中rep指向的weaks引用计数减少1为1;
之后析构res2,uses由1减少为0,weaks由1减少为0,所以释放引用计数器;析构ptr指向的资源,将weaks由1降为0;释放引用计数器。
多个线程能在 shared_ptr
的不同实例上调用所有成员函数(包含复制构造函数与复制赋值)而不附加同步,即使这些实例是副本,且共享同一对象的所有权。若多个执行线程访问同一 shared_ptr
而不同步,且任一线程使用 shared_ptr
的非 const 成员函数,则将出现数据竞争;原子函数的 shared_ptr 特化能用于避免数据竞争。或者是进行同步操作。
多线程下的智能指针读操作不会产生线程安全问题。
1.2.3.3多线程下的智能指针写操作会产生线程安全问题
shared_ptr& Message::GetInstance()
{
std::lock_guardlock(m_mtx);
m_instance = std::shared_ptr(new Message);
m_instance->AddWorkthread();
return m_instance;
}
void Message::AddWorkthread()
{
static weak_ptrwp = shared_from_this();
pwth =new thread (&Message::WorkThread,ref(wp));
}
void Message::WorkThread(std::weak_ptr&pm)
{
while (true)
{
shared_ptr pa = pm.lock();
if (!pa)return;
std::lock_guardlock(pa->ms_mtx);
if (pa->m_stop)return;
if (!pa->m_message.empty())
{
std::cout << pa->m_message << std::endl;
pa->m_message.clear();
}
}
}
void Message::AddWorkthread()函数中的创建线程函数中传入的weak_ptr参数是引用传递时,要注意的是此智能指针的生存期。看如下例子
void Message::AddWorkthread()
{
weak_ptrwp = shared_from_this();
pwth =new thread (&Message::WorkThread,ref(wp));
}
将全局的wp改成局部,那么当启动线程时,wp被析构,程序崩溃。
void Message::AddWorkthread()
{
weak_ptrwp = shared_from_this();
pwth =new thread (&Message::WorkThread,wp);
//m_tha.detach();
}
void Message::WorkThread(std::weak_ptrpm)
{
while (true)
{
shared_ptr pa = pm.lock();
if (!pa)return;
std::lock_guardlock(pa->ms_mtx);
if (pa->m_stop)return;
if (!pa->m_message.empty())
{
std::cout << pa->m_message << std::endl;
pa->m_message.clear();
}
}
}
最常见的使用错误:
1.使用相同的内置指针初始化多个智能指针
这会导致多个智能指针并不会指向同一个引用计数器。
2.不要用裸指针作为智能指针的参数传入
void func(shared_ptr ptr) {
return;
}
int* p = new int(100);//裸指针
func(shared_ptr(p));
ptr在函数栈被释放时,资源也被立即释放。而你之后再继续使用那不就爆炸了嘛。
3.不要轻易使用get()取得裸指针。
智能指针就是怕裸指针出事情才出现的。所以最好不要做此操作。
4.使用shared_from_this返回this是安全的
先看一个不安全的例子:
struct Bad
{
std::shared_ptr getptr() {
return std::shared_ptr(this);
}
~Bad() { std::cout << "Bad::~Bad() called\n"; }
};
void testBad()
{
// Bad, each shared_ptr thinks it's the only owner of the object
std::shared_ptr bad0 = std::make_shared();
std::shared_ptr bad1 = bad0->getptr();
std::cout << "bad1.use_count() = " << bad1.use_count() << '\n';
} // UB: Bad 的二次删除
int main()
{
testBad();
}
bad1的构建其实和bad0并非同一个引用技术器。所以最后依次析构时资源被释放两次。
5.栈空间创建的对象却用Sared_from_this返回this
struct Good : std::enable_shared_from_this // 注:公开继承
{
std::shared_ptr getptr() {
return shared_from_this();
}
};
void misuseGood()
{
//坏:调用 shared_from_this 但没有 std::shared_ptr 占有调用者
try {
Good not_so_good;
std::shared_ptr gp1 = not_so_good.getptr();
std::cout << gp1 << std::endl;
}
catch (std::bad_weak_ptr& e) {
// 未定义行为(C++17 前)/抛出 std::bad_weak_ptr (C++17 起)
std::cout << e.what() << '\n';
}
}
int main()
{
misuseGood();
}
非new出的空间就不要使用智能指针了。