资阳网站设计,北京网站快速排名优化,云梦网络 网站模板,数据查询网站建设unique_ptr
一个unique_ptr“拥有”它所指向的对象。
与shared_ptr不同#xff0c;某个时刻只能有一个unique_ptr指向一个给定对象。
当unique_ptr被销毁时#xff0c;它所指向的对象也被销毁。
和shared_ptr 不同#xff0c;没有类似make_shared的标准库函数返回一个un…unique_ptr
一个unique_ptr“拥有”它所指向的对象。
与shared_ptr不同某个时刻只能有一个unique_ptr指向一个给定对象。
当unique_ptr被销毁时它所指向的对象也被销毁。
和shared_ptr 不同没有类似make_shared的标准库函数返回一个unique ptr
当我们定义一个unique_ptr时需要将其绑定到一个new返回的指针
类似shared_ptr初始化unique_ptr必须采用直接初始化形式
unique_ptrdouble p1; // 可以指向一个double的unique_ptr
unique_ptrint p2(new int(42));// p2指向一个值为 42的int
由于一个unique_ptr拥有它指向的对象因此unique_ptr不支持普通的拷贝或赋值操作
unique_ptrstring pl(new string(Stegosaurus));
unique_ptrstring p2(p1);// 错误unique_ptr不支持拷贝unique_ptrstring p3;
p3 p2; //错误unique_ptr不支持赋值
unique_ptr操作 unique_ptrTu1空智能指针可以指向类型为T的对象p将p作为一个条件判断若p指向一个对象则为true*p解引用p获得它指向的对象p-mem等价于(*p).memp.get()返回p中保存的指针。要小心使用若智能指针释放了其对象返回的指针所指的对象也就消失了 swap(p,q) p.swap(q) 交换p和q的指针unique ptrT, D u2空unique_ptr可以指向类型为T。u1会使用delete来释放它的指针unique_ptrT, D u(d)空unique_ptr指向类型为T的对象用类型为D的对象d代替deleteu nullptr释放u指向的对象将u置为空u.release()u放弃对指针的控制权u.reset(q) u.reset (nullptr)如果提供了内置指针q令u指向这个对象否则将u置为空u.reset()释放u指向的对象
虽然我们不能拷贝或赋值unique_ptr但可以通过调用release或reset 将指针的所有权从一个(非const) unique_ptr转移给另一个unique:
// 将所有权从 pl(指向 string Stegosaurus)转移给p2
unique_ptrstringp2(pl.release());// release将p1置为空
unique_ptrstring p3(new string(Trex));
// 将所有权从p3转移给p2
p2.reset(p3,release());// reset释放了p2原来指向的内存
release 成员返回unique_ptr当前保存的指针并将其置为空。
因此p2被初始化为p1原来保存的指针而p1被置为空。
reset成员接受一个可选的指针参数令unique_ptr重新指向给定的指针。如果unique_ptr不为空它原来指向的对象被释放。
因此对 p2 调用 reset 释放了用Stegosaurus”初始化的string所使用的内存将p3对指针的所有权转移给p2并将p3置为空。
调用release 会切断 unique_ptr和它原来管理的对象间的联系。release返回的指针通常被用来初始化另一个智能指针或给另一个智能指针赋值。
在本例中管理内存的责任简单地从一个智能指针转移给另一个。但是如果我们不用另一个智能指针来保存release返回的指针我们的程序就要负责资源的释放
p2.release(); //错误p2不会释放内存而且我们丢失了指针
auto p p2.release();// 正确但我们必须记得 delete(p)
传递 unique_ptr 参数和返回 unique_ptr
不能拷贝 unique_ptr的规则有一个例外我们可以拷贝或赋值一个将要被销毁的unique_ptr。最常见的例子是从函数返回一个unique_ptr:
unique_ptrint clone (int p)
{
/正确从int创建一个unique_ptrint
return unlque_ptrint(new int(p));}
还可以返回一个局部对象的拷贝
unique_ptrint clone(int p)
{
unique_ptrint ret(new int (p));
return ret;
}
对于两段代码编译器都知道爱返回的对象将要被销毁。在此情况下编译器执行一种特殊的“拷贝”. 向后兼容auto_ptr 标准库的较早版本包含了一个名为auto_ptr的类它具有unique_ptr的部分特性但不是全部。特别是我们不能在容器中保存auto_ptr也不能从函数中返回auto_ptr。 虽然auto_ptr仍是标准库的一部分但编写程序时应该使用unique_ptr。 向unique_ptr传递删除器
类似 shared_ptr, unique_ptr 默认情况下用delete释放它指向的对象。
与shared_ptr一样我们可以重载一个unique_ptr中默认的删除器。但是unique_ptr管理删除器的方式与shared_ptr不同。
重载一个 unique_ptr 中的删除器会影响到 unique_ptr类型以及如何构造(或reset)该类型的对象。与重载关联容器的比较操作类似我们必须在尖括号中unique_ptr指向类型之后提供删除器类型。
在创建或reset一个这种unique_ptr类型的对象时必须提供一个指定类型的可调用对象(删除器) std::unique_ptr 允许你提供一个自定义的删除器这个删除器会在 unique_ptr 被销毁时调用。
以下是一个使用自定义删除器的 std::unique_ptr 的例子
#include iostream
#include memory // 自定义删除器这里我们简单地将 delete 替换为 cout 输出
struct CustomDeleter { void operator()(int* ptr) const { std::cout Deleting int with value: *ptr std::endl; delete ptr; }
}; int main() { // 使用自定义删除器创建 unique_ptr std::unique_ptrint, CustomDeleter smartPtr(new int(5)); // 输出智能指针指向的值 std::cout Value of int: *smartPtr std::endl; // 当 smartPtr 超出作用域时CustomDeleter 的 operator() 将被调用 // 这将输出 Deleting int with value: 5 并删除 int return 0;
}
在这个例子中我们定义了一个 CustomDeleter 结构体它重载了 operator() 以接受一个 int* 参数。当 std::unique_ptr 被销毁时它会调用这个 operator() 来删除其指向的对象。在这个例子中我们只是简单地输出了一个消息并调用了 delete 来释放内存。但是你可以根据需要实现任何逻辑。
weak_ptr
weak ptr 是一种不控制所指向对象生存期的智能指针它指一个由shared_ptr 管理的对象。
将一个 weak_ ptr 绑定到一个shared_ptr 不会改shared_ptr的引用计数。
一 旦最后一个指向对象的shared_ptr被销毁对象就会释放。
即使有 weak_ptr指向对象对象也还是会被释放。
因此weak_ptr的名字住了这种智能指针“弱”共享对象的特点。
weak_ptr weak_ptrT1空weak_ptr可以指向类型为T的对象weak_ptrT w(sp)与shared ptr sp指向相同对象的weak_ptr。T必须能转换为sp指向的类型wpp可以是一个shared_ptr或一个weak_ptr。赋值后w与p共享对象w.reset()将w置为空w.use_count()与w共享对象的shared_ptr的数量w.expired()若w.use_count()为0返回true否则返回falsew.lock()如果expired为true返回一个空shared_ptr否则返回一个指向w的对象的shared_ptr
当我们创建一个weak_ptr时要用一个shared_ptr来初始化它
auto p make_sharedint(42);
weak_ptrint wp(p);//wp弱共享pP的引用计数未改变
本例中wp和p指向相同的对象。由于是弱共享创建wp不会改变p的引用计数wp指向的对象可能被释放掉。
由于对象可能不存在我们不能使用weak_ptr直接访问对象而必须调用lock。
此函数检查weak_ptr指向的对象是否仍存在。如果存在lock返回一个指向共享对象的shared_ptr。
与任何其他shared_ptr类似只要此shared_ptr存在它所指向的底层对象也就会一直存在。
例如
if (shared_ptrint np wp.lock())
{//如果np不为空则条件成立//在if中np与p共享对象
}
在这段代码中只有当lock调用返回true时我们才会进入语句体。在f中使用np访问共享对象是安全的。
weak_ptr的用途 weak_ptr 的主要用途是打破 shared_ptr 之间的循环引用从而防止内存泄漏。
在 C 中当使用 shared_ptr 管理动态分配的内存时每个 shared_ptr 都会持有一个引用计数该计数表示有多少个 shared_ptr 实例指向同一个对象。只有当最后一个 shared_ptr 被销毁或重置时所指向的对象才会被删除。
然而当两个或多个 shared_ptr 实例相互引用时就会形成一个循环引用。这意味着每个 shared_ptr 都认为另一个 shared_ptr 仍然持有对象的引用因此对象的引用计数永远不会降到零即使逻辑上已经没有代码再需要这个对象。这会导致内存泄漏因为对象永远不会被删除。
weak_ptr 就是为了解决这个问题而设计的。它是对一个由shared_ptr 管理的对象的弱引用这意味着它不控制对象的生命周期。weak_ptr 不拥有所指向的对象它不会增加对象的引用计数。相反它观察 shared_ptr 的引用计数并在最后一个 shared_ptr 被销毁时变得无效。
通过使用 weak_ptr 来替代 shared_ptr 中的一个或多个引用可以打破循环引用。当循环中的一个 shared_ptr 不再存在时即使其他 shared_ptr 通过 weak_ptr 仍然可以“观察”到对象对象的引用计数也会减少到零从而触发对象的删除。
举例
下面是一个具体的例子展示了std::weak_ptr如何用于解决循环引用的问题。
假设我们有一个简单的Parent和Child类每个Parent对象可以有一个Child对象每个Child对象都有一个指向其Parent的指针。如果我们使用shared_ptr来管理这些对象的生命周期就可能会遇到循环引用的问题。
#include iostream
#include memory class Child; class Parent {
public: std::shared_ptrChild child; ~Parent() { std::cout Parent destroyed std::endl; }
}; class Child {
public: // 使用 weak_ptr 而不是 shared_ptr 来避免循环引用 std::weak_ptrParent parent; ~Child() { std::cout Child destroyed std::endl; }
}; int main() { // 创建 Parent 和 Child 对象并设置相互引用 auto parent std::make_sharedParent(); auto child std::make_sharedChild(); parent-child child; child-parent parent; // 使用 weak_ptr 而不是 shared_ptr // 此时parent 和 child 相互引用但由于 child 使用的是 weak_ptr不会增加 Parent 对象的引用计数 // 当 parent 和 child 超出作用域并被销毁时... // ...由于 child 使用的是 weak_ptr它不会阻止 Parent 对象的销毁 // ...Parent 对象首先被销毁然后 Child 对象也被销毁 // 输出 Parent destroyed然后输出 Child destroyed // 证明即使存在相互引用对象也能被正确删除没有内存泄漏 return 0;
}
在这个例子中Parent 类有一个指向 Child 的 shared_ptr 成员而 Child 类有一个指向 Parent 的weak_ptr 成员。当 parent 和 child 超出作用域时由于 child 使用是 weak_ptr它不会增加 Parent 对象的引用计数。因此当 parent 被销毁时没有其他的 shared_ptr 指向 Parent 对象所以 Parent 对象也会被销毁。之后使 child 的 weak_ptr 成员仍然“观察”着已经销毁的 Parent 对象它也不会阻止 Child 对象的销毁。最终Child 对象也会被正确删除。
通过使用 std::weak_ptr我们打破了 Parent 和 Child 之间的循环引用确保了即使存在相互引用对象的生命周期也能被正确管理从而避免了内存泄漏。