烟台做网站哪里好,东营市东营网站设计,iis怎么使用来建设一个网站,广州建站网络公司智能指针 一、内存泄露1.1 内存泄露常见原因1.2 如何避免内存泄露 二、实例Demo2.1 文件结构2.2 Dog.h2.3 Dog.cpp2.3 mian.cpp 三、独占式智能指针:unique _ptr3.1 创建方式3.1.1 ⭐从原始(裸)指针转换#xff1a;3.1.2 ⭐⭐使用 new 关键字直接创建#xff1a;3.1.3 ⭐⭐⭐… 智能指针 一、内存泄露1.1 内存泄露常见原因1.2 如何避免内存泄露 二、实例Demo2.1 文件结构2.2 Dog.h2.3 Dog.cpp2.3 mian.cpp 三、独占式智能指针:unique _ptr3.1 创建方式3.1.1 ⭐从原始(裸)指针转换3.1.2 ⭐⭐使用 new 关键字直接创建3.1.3 ⭐⭐⭐使用 std::make_unique推荐3.1.4 对比使用 std::make_unique 和 使用 new 关键字直接创建 3.2 unique的特性3.2.1 独占所有权3.2.2 不可复制、可移动3.2.3 自动内存管理3.2.4 自定义删除器3.2.5 支持数组3.2.6 与标准库的兼容性 四、共享计数指针shared_ptr4.1 实例4.2 shared_ptr 的特性4.2.1 共享所有权4.2.2 引用计数4.2.3自动内存管理4.2.4 线程安全的引用计数4.2.5 使用方便4.2.6 支持自定义删除器4.2.7std::weak_ptr 结合使用 五、弱引用智能指针weak_ptr5.1 循环依赖5.2 weak_ptr 的特性5.2.1 不增加引用计数5.2.2 防止循环引用5.2.3 可以转换为 shared_ptr5.2.4 允许检查资源状态5.2.5 与 shared_ptr 共享同一控制块5.2.6 线程安全 前言参加工作两年来感觉还没咋使用过智能指针今天还是来总结学习下以后总有天会用到的 一、内存泄露
在C中内存泄露指的是程序在运行过程中动态分配内存使用new或malloc后没有释放相应的内存使用delete或free导致这些内存空间无法被重新使用从而造成系统资源的浪费最终可能导致程序在长时间运行后耗尽可用内存。
1.1 内存泄露常见原因
①忘记释放内存 在使用动态内存分配后程序的某些路径可能会遗漏释放内存的代码。
void example() { int* arr new int[10]; // 动态分配内存 // ... 使用 arr // delete[] arr; // 忘记释放内存
} ②异常处理 在分配内存之后如果发生异常且没有正确处理异常会导致内存未释放。
void example() { int* arr new int[10]; // 假设这里发生异常 throw std::runtime_error(Error); delete[] arr; // 这一行永远不会被执行
} ③指针丢失 将指针赋值为其他指针时如果没有先释放原有指针指向的内存就会造成内存泄漏。
void example() { int* ptr new int(42); ptr new int(24); // 原来的内存没有释放造成泄漏 delete ptr; // 只释放了新的内存
} 容器管理在使用标准库容器如std::vectorstd::map等时注意对存储的指针管理不要在容器中存储动态分配的原始指针建议使用智能指针。
1.2 如何避免内存泄露
①使用智能指针C11引入了std::unique_ptr和std::shared_ptr它们自动管理内存减少内存泄露的风险。
#include memory void example() { std::unique_ptrint[] arr(new int[10]); // 使用智能指针 // 不需要手动释放内存超出作用域时自动释放
} ②确保配对每次使用new或malloc时确保有相应的delete或free。
③RAII资源获取即初始化将资源的生命周期与对象的生命周期绑定避免手动管理内存。
④使用内存检查工具使用工具如Valgrind、AddressSanitizer等进行内存错误检测帮助发现内存泄露和其它内存管理问题。
通过以上措施可以大大减少C程序中的内存泄露问题
二、实例Demo
2.1 文件结构 2.2 Dog.h
#ifndef DOG_H
#define DOG_H
/*这里使用条件编译指令防止 重复引用头文件 达到目的:头文件保护或包含保护也可以用#pragma once 但是使用该方式更具备兼容性*/
#includestring
#includeiostream
class Dog
{
public:Dog(const std::string name);Dog() default;/* 这里用了关键字defalut 保留编译器原有的构造函数*/~Dog();void dog_info() const/*使用const 关键字确保该函数只读该类成员*/{std::cout U Dog name is: this-m_name std::endl;}std::string get_name () const{return m_name;}void set_name(const std::string u_name) /*使用const引用 确保u_name 只读且通过引用方式传值提高效率,避免非必要的拷贝*/{m_name u_name;}
private:std::string m_name bigYellow;
};
#endif2.3 Dog.cpp
#include Dog.hDog::Dog(const std::string name):m_name(name)
{std::cout 构造一根狗子,它叫 m_name std::endl;
}Dog::~Dog()
{std::cout 析构一根狗子,它叫 m_name std::endl;
}2.3 mian.cpp
#include iostream
#include Dog.h
#include memory/*使用智能指针的头文件*/
using namespace std;int main(int argc,char** argv)
{return 0;
}三、独占式智能指针:unique _ptr
在C中智能指针是用来管理动态分配内存的对象以减少内存泄漏和资源管理的复杂性。独占式指针std::unique_ptr是C11标准库中引入的一种智能指针它的主要特点是保证对其所管理的资源具有唯一的所有权。
3.1 创建方式
3.1.1 ⭐从原始(裸)指针转换
通过 std::unique_ptr 的构造函数或 std::unique_ptr 的 reset 方法可以将一个现有的原始(裸)指针转换为 unique_ptr。
#include iostream
#include Dog.h
#include memory/*使用智能指针的头文件*/
using namespace std;int main(int argc,char** argv)
{/*方式①:通过原始(裸)指针*/Dog* gua new Dog(GUA);unique_ptrDog dogUniquePtr(gua);// 将已有的裸指针分配给unique_ptr gua-set_name(XI);gua-dog_info();dogUniquePtr-dog_info();return 0;
}运行结果: 我们不妨将这两个指针打印出来
#include iostream
#include Dog.h
#include memory/*使用智能指针的头文件*/
using namespace std;int main(int argc,char** argv)
{/*方式①:通过原始(裸)指针*/Dog* gua new Dog(GUA);unique_ptrDog dogUniquePtr(gua);// 将已有的裸指针分配给unique_ptr gua-set_name(XI);gua-dog_info();dogUniquePtr-dog_info();cout gua的地址: gua endl;cout dogUniquePtr的地址: dogUniquePtr.get() endl;//delete gua;return 0;
}运行结果 两个指针其实都指向了同一块地址但是我们发现这块地址任然可以被两个指针操作这也能叫独占智能指针吗安全吗这如果我释放了gua的地址会怎么样解开上述实例代码最后一行执行 运行结果: 程序崩溃 结论 这种方式虽然会自动释放内存地址但是原始(裸)指针仍然可以操作该地址。 如果尝试使用已被 std::unique_ptr 释放的内存会导致未定义行为。 如果你在裸指针上 delete 了内存而后又让 std::unique_ptr 执行析构就会发生双重释放的问题这将会导致程序崩溃。 所以尽管 std::unique_ptr 是设计为独占式的但如果需要在你的代码中使用裸指针必须非常小心确保不会在裸指针和 std::unique_ptr 之间发生冲突。通常情况下最佳实践是尽量减少对于裸指针的使用而是使用智能指针进行内存管理以确保资源的安全和有效释放。
3.1.2 ⭐⭐使用 new 关键字直接创建
#include iostream
#include Dog.h
#include memory/*使用智能指针的头文件*/
using namespace std;int main(int argc,char** argv)
{/*方式②:通过new关键字创建*/unique_ptrDog jl{ new Dog(jl) };jl-dog_info();return 0;
}运行结果:
3.1.3 ⭐⭐⭐使用 std::make_unique推荐
C14 添加了 std::make_unique 函数能够更安全地创建 unique_ptr。
#include iostream
#include Dog.h
#include memory/*使用智能指针的头文件*/
using namespace std;int main(int argc,char** argv)
{/*方式③:通过td::make_unique推荐创建*/unique_ptrDog zzy std::make_uniqueDog(zzy);zzy-dog_info();zzy-set_name(bigZZY);zzy-dog_info();return 0;
}运行结果:
3.1.4 对比“使用 std::make_unique” 和 “使用 new 关键字直接创建”
① 可读性和简洁性
new 关键字
std::unique_ptrint ptr(new int(42)); std::make_unique
auto ptr std::make_uniqueint(42); 使用 std::make_unique更加简洁、可读将所有权的转移和对象的构造合并为一行代码同时也更加简洁减少了冗余的 new 关键字。
② 安全性 new 关键字在使用 new 时可能发生内存分配失败的情况此时new会抛出 std::bad_alloc 异常。创建 std::unique_ptr 的时候如果 new 返回 nullptr则需要开发者在代码中处理这些异常。
std::make_uniquestd::make_unique 不会返回 nullptr而是直接抛出异常因此代码更加安全并且没有风险因为 std::unique_ptr 会自动管理内存即使在构造失败的情况下也不会泄漏内存。
③ 内存泄漏风险 new 关键字如果使用 new 创建一个对象并且由于某种错误例如异常抛出没有成功将其指针传递给 std::unique_ptr则会发生内存泄漏。
std::unique_ptrint ptr;
if (someCondition) { int* rawPtr new int(42); // 如果在此之后抛出异常将会泄漏 ptr.reset(rawPtr);
} // 可能遇到内存泄漏 std::make_unique通过 std::make_unique 创建对象的过程中确保了不会有内存泄漏的风险因为std::make_unique 会处理所有权的转移并直接返回 std::unique_ptr。
④. 性能 在性能方面std::make_unique 一般情况下是更优的选择尽管对于大多数应用程序它们之间的性能差异可以忽略不计。使用 std::make_unique 可以避免可能的额外操作。
总结: 推荐使用 std::make_unique由于它提高了可读性、安全性并且避免了内存泄漏的风险现代 C 的最佳实践是使用 std::make_unique 来创建和管理动态分配的对象。 仅在特定情况下使用 new当你需要直接获取裸指针或者从其他 API 中传递裸指针时才可能临时使用 new 但也要小心管理内存确保避免内存泄漏。
3.2 unique的特性
3.2.1 独占所有权
std::unique_ptr 提供独占性所有权语义这意味着每个 unique_ptr 对象都可以唯一拥有一个动态分配的资源。这样的设计确保了资源不会被多个指针管理从而避免了双重释放double free等问题 结合实例: /*特性①:独占所有权*/Dog* gua new Dog(GUA);unique_ptrDog dogUniquePtr1(gua);//编译错误不能复制 unique_ptr因为它是独占的//unique_ptrDog dogUniquePtr2 dogUniquePtr1;3.2.2 不可复制、可移动
std::unique_ptr 不支持复制copy即不能使用拷贝构造函数或拷贝赋值运算符因为这会导致所有权的混淆。相反它支持移动语义move可以通过移动构造和移动赋值将资源的所有权从一个 unique_ptr 转移到另一个
std::unique_ptrint ptr1 std::make_uniqueint(10);
std::unique_ptrint ptr2 std::move(ptr1); // ptr1 现在为空ptr2 拥有资源 结合实例
#include iostream
#include Dog.h
#include memory/*使用智能指针的头文件*/
using namespace std;void do_with_pass_dog(unique_ptrDog u_dog)
{coutu_dog 地址是:u_dog.get()endl;u_dog-dog_info();
}
int main(int argc,char** argv)
{/*特性②:不可复制、可移动*/unique_ptrDog zzy std::make_uniqueDog(zzy);//do_with_pass_dog(zzy);cout zzy 地址是: zzy.get() endl;do_with_pass_dog(std::move(zzy));cout zzy 地址是: zzy.get() endl;return 0;
}运行结果: 不难发现 通过move将该智能指针管理的地址交接给了宁外一个智能指针这种特性保证了只有一个智能指针管理一块地址满足独占指针的特性。
3.2.3 自动内存管理
当 std::unique_ptr 的生命周期结束时它所持有的资源会自动被释放。这种自动释放机制减少了手动管理内存的需要降低了内存泄漏风险
{ std::unique_ptrint ptr(new int(10)); // ptr 指向新分配的整数
} //ptr 到达作用域末尾自动释放内存3.2.4 自定义删除器
std::unique_ptr 可以接受一个自定义的删除器这使得它不仅局限于标准类型的资源管理。例如可以在资源释放时执行特定的逻辑
std::unique_ptrFILE, decltype(fclose) filePtr(fopen(example.txt, r), fclose); 3.2.5 支持数组
std::unique_ptr 可以用来管理动态数组。使用 std::unique_ptrT[] 来确保数组的正确释放
std::unique_ptrint[] arrPtr(new int[10]); // 管理动态分配的整型数组 3.2.6 与标准库的兼容性
std::unique_ptr 可以与 C 标准库中的其他组件良好协作例如可以用在 STL 容器如 std::vector中从而构建复杂的数据结构
std::vectorstd::unique_ptrint vec;
vec.push_back(std::make_uniqueint(10)); 四、共享计数指针shared_ptr
std::shared_ptr 是 C11 引入的智能指针之一属于C标准库中的 memory 头文件。它实现了共享所有权的内存管理方式允许多个 std::shared_ptr实例共同拥有同一个对象。shared_ptr创建了一个计数器与类对象所指的内存相关联 Copy则计数器加一销毁则计数器减一api为use_count() 注意 weak ptr并不拥有所有权 并不能调用- 和解引用*
4.1 实例
#include iostream
#include Dog.h
#include memory/*使用智能指针的头文件*/
using namespace std;
int main(int argc,char** argv)
{/*计数指针*/// 创建一个 shared_ptr指向 MyClass 对象 std::shared_ptrDog ptr1 std::make_sharedDog();ptr1-dog_info();// 创建另一个 shared_ptr指向同一个对象 std::shared_ptrDog ptr2 ptr1;std::cout Reference Count: ptr1.use_count() std::endl; // 输出: 2 return 0;
}运行结果: ①初始化 :std::shared_ptr使用 std::make_shared 创建一个 shared_ptr ptr1指向 MyClass 的对象。
②共享所有权ptr2 是通过拷贝 ptr1 创建的它们共同拥有同一个 MyClass 对象。此时引用计数变为。
③引用计数 使用 use_count() 方法可以查看有多少个shared_ptr在共享同一个对象。
④自动释放资源 当 ptr1 和ptr2在作用域结束后引用计数降为零MyClass 对象的内存将自动被释放调用其析构函数。
4.2 shared_ptr 的特性
4.2.1 共享所有权
多个 std::shared_ptr 实例可以共同管理同一个动态分配的对象每个指针都有对这个对象的所有权。
#include iostream
#include memory struct Object { int value; Object(int v) : value(v) {}
}; int main() { std::shared_ptrObject ptr1 std::make_sharedObject(10); std::shared_ptrObject ptr2 ptr1; // ptr2 共享 ptr1 的所有权 std::cout Value from ptr1: ptr1-value std::endl; std::cout Value from ptr2: ptr2-value std::endl; return 0;
}4.2.2 引用计数
每个 std::shared_ptr 都维护一个引用计数用于跟踪有多少个 shared_ptr 实例指向同一个对象。当引用计数降为零时指向的对象会被自动释放。
#include iostream
#include memory int main() { std::shared_ptrint countPtr std::make_sharedint(42); std::cout Reference Count: countPtr.use_count() std::endl; // 输出 1 { std::shared_ptrint anotherPtr countPtr; std::cout Reference Count: countPtr.use_count() std::endl; // 输出 2 } // anotherPtr 超出作用域 std::cout Reference Count: countPtr.use_count() std::endl; // 输出 1 return 0;
}4.2.3自动内存管理
在 std::shared_ptr 超出作用域或被重置时会自动释放关联的对象减少内存泄漏的风险。
#include iostream
#include memory struct Resource { Resource() { std::cout Resource acquired std::endl; } ~Resource() { std::cout Resource released std::endl; }
}; int main() { { std::shared_ptrResource resourcePtr std::make_sharedResource(); // 资源在这里管理 } // resourcePtr 超出作用域资源被自动释放 return 0;
}4.2.4 线程安全的引用计数
对引用计数的操作是线程安全的。但需要注意指向的对象内容本身不是线程安全的。
#include iostream
#include memory
#include thread
void threadFunction(std::shared_ptrint ptr) { std::cout Thread value: *ptr std::endl;
}
int main() { std::shared_ptrint sharedData std::make_sharedint(20); std::thread t1(threadFunction, sharedData); std::thread t2(threadFunction, sharedData); t1.join(); t2.join(); return 0;
}4.2.5 使用方便
可以通过 std::make_shared 来简化 shared_ptr 的创建同时提高性能减少内存分配次数。
#include iostream
#include memory struct Simple { Simple() { std::cout Simple constructed std::endl; } ~Simple() { std::cout Simple destructed std::endl; }
}; int main() { auto simplePtr std::make_sharedSimple(); // 自动创建 shared_ptr return 0; // 自动清理
}4.2.6 支持自定义删除器
可以通过构造函数提供自定义删除器以定义对象如何被释放适用于特殊的资源管理需求。
#include iostream
#include memory struct CustomDeleter { void operator()(int* p) { std::cout Custom delete for *p std::endl; delete p; }
}; int main() { std::shared_ptrint ptr(new int(42), CustomDeleter()); return 0; // 会调用 CustomDeleter
}4.2.7std::weak_ptr 结合使用
可与 std::weak_ptr 一起使用以打破循环引用的问题weak_ptr 不增加引用计数。
#include iostream
#include memory struct Node { std::shared_ptrNode next; ~Node() { std::cout Node destroyed std::endl; }
}; int main() { std::shared_ptrNode head std::make_sharedNode(); std::weak_ptrNode weakHead head; // weak_ptr 不增加引用计数 head-next std::make_sharedNode(); // 创建另一个 Node std::cout Reference count of head: head.use_count() std::endl; // 输出 2 head.reset(); // 释放 shared_ptr if (auto temp weakHead.lock()) { std::cout Node is alive. std::endl; } else { std::cout Node has been deleted. std::endl; // 输出 } return 0;
}五、弱引用智能指针weak_ptr
std::weak_ptr 是 C 标准库中的一个智能指针旨在解决与 std::shared_ptr 相关的循环依赖问题。它提供了一种方式来观察共享对象但不会增加其引用计数从而避免了循环引用造成的内存泄漏。
5.1 循环依赖
循环依赖问题通常发生在两个或多个类相互持有对方的引用这会导致它们之间的引用计数无法归零从而引发内存泄漏。如下实例我们创建了两个类 A 和 B它们互相指向对方的实例。
#include iostream
#include memory class B; // 前向声明 class A {
public: std::shared_ptrB b; // 拥有者指针 A() { std::cout A created std::endl; } ~A() { std::cout A destroyed std::endl; }
}; class B {
public: std::shared_ptrA a; // 拥有者指针 B() { std::cout B created std::endl; } ~B() { std::cout B destroyed std::endl; }
}; int main() { std::shared_ptrA a std::make_sharedA(); std::shared_ptrB b std::make_sharedB(); a-b b; // A 拥有 B b-a a; // B 拥有 A return 0; // 当程序结束时A 和 B 永远无法被销毁
}在上述代码中A 类拥有 B 的一个 shared_ptr而 B 类也拥有 A 的一个 shared_ptr。这样就形成了一种循环依赖关系 a 的引用计数为 1指向 A但因为它持有 bB 的引用所以 B 的引用计数也为 1。 b 的引用计数为 1指向 B但因为它持有 aA 的引用所以 A 的引用计数也为 1。 由于这两个类互相持有对方的 shared_ptr引用计数永远不会归零导致内存无法释放。
了解决这个问题我们可以将其中一个 shared_ptr 改为 weak_ptr。通常持有较少使用的引用或者想要避免循环引用的类会使用 weak_ptr。以下是修改后的代码示例
#include iostream
#include memory class B; // 前向声明 class A {
public: std::shared_ptrB b; // 拥有者指针 A() { std::cout A created std::endl; } ~A() { std::cout A destroyed std::endl; }
}; class B {
public: std::weak_ptrA a; // 使用 weak_ptr 避免循环引用 B() { std::cout B created std::endl; } ~B() { std::cout B destroyed std::endl; }
}; int main() { std::shared_ptrA a std::make_sharedA(); std::shared_ptrB b std::make_sharedB(); a-b b; // A 拥有 B b-a a; // B 使用 weak_ptr 指向 A return 0; // 当程序结束时A 和 B 将会被正确销毁
}5.2 weak_ptr 的特性
5.2.1 不增加引用计数
std::weak_ptr 拥有一个指向 std::shared_ptr 管理的对象的弱引用。与 std::shared_ptr 不同它不会增加对象的引用计数。这意味着std::weak_ptr 仅仅作为对对象的观察者使用它不会阻止对象的销毁。
#include iostream
#include memory class A {
public: A() { std::cout A created std::endl; } ~A() { std::cout A destroyed std::endl; }
}; int main() { std::shared_ptrA p1 std::make_sharedA(); // 创建一个 shared_ptr std::weak_ptrA p2 p1; // 从 shared_ptr 创建一个 weak_ptr std::cout p1 use count: p1.use_count() std::endl; // 输出: 1 std::cout p2 expired: p2.expired() std::endl; // 输出: 0 (false) return 0;
} 5.2.2 防止循环引用
std::weak_ptr 是解决循环引用问题的关键。通过将某个类的某些指针通常是指向负责管理资源的类的指针定义为 weak_ptr可以打破这种相互依赖关系允许资源被正确释放。
#include iostream
#include memory class B; // 前向声明 class A {
public: std::shared_ptrB bPtr; // 使用 shared_ptr
}; class B {
public: std::weak_ptrA aPtr; // 使用 weak_ptr 防止循环引用
}; int main() { std::shared_ptrA a std::make_sharedA(); std::shared_ptrB b std::make_sharedB(); a-bPtr b; b-aPtr a; // 不会引起循环引用 return 0; // 程序结束时 A 和 B 的资源会被正确释放
} 5.2.3 可以转换为 shared_ptr
std::weak_ptr 提供了一个 lock() 方法可以将其转换为 std::shared_ptr如果原始对象仍然存在即其引用计数大于零lock() 方法会返回一个有效的 std::shared_ptr。如果对象已被销毁则返回一个空的 shared_ptr。
#include iostream
#include memory class A {
public: A() { std::cout A created std::endl; } ~A() { std::cout A destroyed std::endl; }
}; int main() { std::shared_ptrA p1 std::make_sharedA(); std::weak_ptrA p2 p1; std::shared_ptrA p3 p2.lock(); // 尝试获取 shared_ptr if (p3) { std::cout p3 acquired std::endl; // 将会输出 } p1.reset(); // 释放 p1 的资源 std::shared_ptrA p4 p2.lock(); // 尝试再次获取 shared_ptr if (!p4) { std::cout p4 is null std::endl; // 将会输出 } return 0;
} 5.2.4 允许检查资源状态
通过使用 std::weak_ptr可以检查资源对象的状态。可以使用 expired() 方法来检查指向的对象是否已经被销毁。如果返回 true则表明对象已不再存在。
#include iostream
#include memory class A {
public: A() { std::cout A created std::endl; } ~A() { std::cout A destroyed std::endl; }
}; int main() { std::weak_ptrA p1; { std::shared_ptrA p2 std::make_sharedA(); p1 p2; // p1 指向 p2 std::cout p1 expired: p1.expired() std::endl; // 输出: 0 (false) } std::cout p1 expired after exiting scope: p1.expired() std::endl; // 输出: 1 (true) return 0;
} 5.2.5 与 shared_ptr 共享同一控制块
std::weak_ptr 与其对应的 std::shared_ptr 共享同一个控制块这个控制块保存着引用计数和其他状态信息。这允许 weak_ptr 检查原始对象的状态。
#include iostream
#include memory class A {
public: A() { std::cout A created std::endl; } ~A() { std::cout A destroyed std::endl; }
}; int main() { std::shared_ptrA p1 std::make_sharedA(); std::weak_ptrA p2 p1; // p2 和 p1 共享同一控制块 std::cout p1 use count: p1.use_count() std::endl; // 输出: 1 std::cout p2 use count (via shared_ptr): p2.lock().use_count() std::endl; // 输出: 1 return 0;
}5.2.6 线程安全
std::weak_ptr 和 std::shared_ptr 的操作是线程安全的。可以在多个线程中安全地访问和管理这些智能指针。
#include iostream
#include memory
#include thread
#include vector
#include chrono class A {
public: A() { std::cout A created std::endl; } ~A() { std::cout A destroyed std::endl; }
}; void threadFunction(std::weak_ptrA weakPtr) { // 尝试从 weak_ptr 获取 shared_ptr std::shared_ptrA sharedPtr weakPtr.lock(); if (sharedPtr) { std::cout Thread accessing object std::endl; } else { std::cout Object already destroyed std::endl; }
} int main() { std::shared_ptrA p1 std::make_sharedA(); // 创建一个 weak_ptr 来观察 p1 std::weak_ptrA p2 p1; // 启动多个线程来访问 p2 std::vectorstd::thread threads; for (int i 0; i 5; i) { threads.emplace_back(threadFunction, p2); } // 等待一段时间然后重置 p1模拟对象的销毁 std::this_thread::sleep_for(std::chrono::milliseconds(100)); p1.reset(); // 释放 p1 的资源 // 等待所有线程完成 for (auto t : threads) { t.join(); } return 0;
} ①代码说明 class A定义了一个简单的类 A其构造和析构函数会打印消息以便我们跟踪对象的创建与销毁过程。 threadFunction每个线程执行的函数尝试通过调用 weakPtr.lock() 获取对象的 shared_ptr 如果成功获取 shared_ptr表示对象仍然存在线程可以安全访问该对象。 如果对象已经被销毁lock() 返回空指针线程会打印相关消息。
②主函数中的逻辑 创建一个 std::shared_ptrA 实例 p1并从中获得一个 std::weak_ptrA 实例 p2。 启动多个线程每个线程都尝试访问同一个 weak_ptr。 主线程等待一段时间然后重置 p1模拟对象的销毁。 等待所有线程结束执行。
③线程安全注意事项 使用 std::weak_ptr 来访问 std::shared_ptr避免了因对象销毁而引起的悬挂指针。 weak_ptr.lock() 是线程安全的可以安全地用于多个线程同时访问的场景。 在整个程序运行过程中你将看到某些线程能够访问对象而在对象被销毁后其他线程则会看到对象已经被销毁。