您的位置:首页 > 健康 > 养生 > 电脑上制作网站的软件_北京工程造价信息网官网_拉新app推广接单平台_快速排名工具免费查询

电脑上制作网站的软件_北京工程造价信息网官网_拉新app推广接单平台_快速排名工具免费查询

2024/12/22 20:18:47 来源:https://blog.csdn.net/weixin_45459648/article/details/143204171  浏览:    关键词:电脑上制作网站的软件_北京工程造价信息网官网_拉新app推广接单平台_快速排名工具免费查询
电脑上制作网站的软件_北京工程造价信息网官网_拉新app推广接单平台_快速排名工具免费查询

目录

    • 内存管理问题
    • RALL
    • 智能指针
    • 为什么shared_ptr共享指针会出现循环引用问题,以及怎么解决
    • make_shared与显式通过构造函数初始化(new)的shared_ptr区别?
      • new 来创建对象额外开销
      • make_shared 的优点:
    • 智能指针的实现?(share)

内存管理问题

动态内存管理经常会出现两种问题:
① 忘记释放内存,会造成内存泄漏
② 尚有指针引用内存的情况下就释放了它,产生指针悬挂

RALL

RAII是C++中的一个惯用法,即“Resource Acquisition Is Initialization”,翻译为“资源获取就初始化”。**在构造函数中申请分配资源,在析构函数中释放资源。**因为C++的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在RAII的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定。RAII的核心思想是将资源或者状态与对象的生命周期绑定,通过C++的语言机制,实现资源和状态的安全管理,智能指针是RAII最好的例子

智能指针

智能指针本质上是一个对象,里面封装了普通指针,就是RALL技术
智能指针就可以方便我们控制指针对象的生命周期。在智能指针中,一个对象什么情况下被析构或被删除,是由指针本身决定的,并不需要用户进行手动管理

unique_ptr独享指针
unique_ptr没有复制构造函数,不支持普通的拷贝和赋值操作
unique最常见的使用场景,就是替代原始指针,为动态申请的资源提供异常安全保证。
只要unique_ptr创建成功,unique_ptr对应的析构函数都能保证被调用,从而保证申请的动态资源能被释放掉。

class MyClass {
public:MyClass() ~MyClass()
};int main() {std::unique_ptr<MyClass> ptr1(new MyClass()); // 创建独占指针// std::unique_ptr<MyClass> ptr2 = ptr1; // 错误,不能复制std::unique_ptr<MyClass> ptr2 = std::move(ptr1); // 移动所有权//nuique_ptr是一个模板类,Myclass是指针管理的对象资源是模板类的参数,这两个告诉编译器怎么编译//move(ptr1)将ptr1转换成右值,返回一个右值引用,ptr2 将接管 ptr1 所指向的对象的所有权。之后,ptr2 成为唯一拥有该对象的指针。ptr1指空if (!ptr1) {std::cout << "ptr1 is now null\n"; // ptr1 已经失去所有权}return 0; // 当ptr2超出作用域时,MyClass会被自动销毁
}

shared_ptr共享指针
允许多个指针指向同一个对象。
当对象的所有权需要共享(share)时,share_ptr可以进行赋值拷贝,每一个shared_ptr的拷贝都指向相同的内存
内部使用计数机制维护,每使用他一次,内存的引用计数加1,每析构一次,内部的引用计数减1,减为0时,删除所指向的堆内存。

class MyClass {
public:MyClass() ~MyClass() 
};int main() {std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>(); // 使用make_shared创建共享指针 //初始化智能指针ptr1,引用为1{std::shared_ptr<MyClass> ptr2 = ptr1; // 共享所有权//ptr1 的引用计数从 1 增加到 2。//ptr2 现在也与 ptr1 共享这个计数器,所以它们的引用计数都是 2。std::cout << "Reference count: " << ptr2.use_count() << "\n"; //ptr2是在{}定义的,作用域在{}内} // ptr2超出作用域,它的析构函数被调用,引用计数减少到 1,失去对资源的引用。//ptr1的作用域是整个main函数std::cout << "Reference count after ptr2 is out of scope: " << ptr1.use_count() << "\n"; // ptr1仍保持对资源的引用,技术1return 0; // 当ptr1超出作用域时,MyClass会被自动销毁
}

weak_ptr
weak_ptr 比较特殊,它主要是为了配合shared_ptr而存在的。它不能访问对象,只能观测shared_ptr的引用计数,防止出现死锁。

#include <iostream>
#include <memory>//是 C++ 标准库中的一个头文件,用于提供智能指针的定义和功能。class B; // 前向声明class A {
public:std::shared_ptr<B> b_ptr; // 共享指针指向 B~A()
};class B {
public:std::weak_ptr<A> a_ptr; // 弱指针指向 A~B()
};int main() {// 创建 A 的共享指针std::shared_ptr<A> a = std::make_shared<A>();// 创建 B 的共享指针,并将其保存到 A 的成员中std::shared_ptr<B> b = std::make_shared<B>();a->b_ptr = b;// 将 B 中的 weak_ptr 指向 Ab->a_ptr = a; // 此时 a 和 b 之间建立了弱引用关系std::cout << "Reference count of A: " << a.use_count() << "\n"; // 输出引用计数为 1std::cout << "Reference count of B: " << b.use_count() << "\n"; // 输出引用计数为 1// weak_ptr 不增加引用计数if (auto a_locked = b->a_ptr.lock()) { // 尝试从 weak_ptr 获取 shared_ptrstd::cout << "A is still alive\n";} else {std::cout << "A is already destroyed\n";}/*.lock() 方法:当你调用 lock() 方法时,它会尝试获取一个指向 weak_ptr 管理的对象的 shared_ptr。
如果 weak_ptr 所指向的对象仍然存在(即至少有一个 shared_ptr 指向它,引用计数大于 0),则 lock() 会返回一个有效的 shared_ptr,指向该对象。
如果 weak_ptr 所指向的对象已经被销毁(即引用计数为 0),lock() 会返回一个空的 shared_ptr,可以通过检查返回值来判断对象是否仍然有效。*/// 当 a 和 b 超出作用域时,A 和 B 的析构函数会被调用return 0; 
}

weak_ptr存在的意义到底是什么呢?

weak指针的出现是为了解决shared指针循环引用造成的内存泄漏的问题。由于shared_ptr是通过引用计数来管理原生指针的,那么最大的问题就是循环引用(因为它们都在互相等待对方先释放,所以造成内存泄漏。),这样必然会导致内存泄露(无法删除)。而weak_ptr不会增加引用计数,因此将循环引用的一方修改为弱引用,可以避免内存泄露。

为什么shared_ptr共享指针会出现循环引用问题,以及怎么解决

当两个或多个对象通过shared_ptr相互引用时,它们的引用计数会互相增加,从而导致内存无法被释放。

比如说:
两个类,a和b都被创建,并且它们互相持有对方的shared_ptr。
a的引用计数和b的引用计数都增加到2。
当main结束时,a和b的引用计数不会归零,导致它们永远存在于内存中,造成内存泄漏。

解决:
将其中一个对象的指针改为weak_ptr,以打破循环引用。
a和b的引用计数可以正常减少到0,从而调用它们的析构函数,正确释放内存。

make_shared与显式通过构造函数初始化(new)的shared_ptr区别?

显式通过构造函数初始化:

std::shared_ptr<MyClass> ptr(new MyClass(constructor_args));

这种方式显式地使用new来创建对象并将其传递给shared_ptr。在这种情况下,指针内存的分配和控制块的分配是分开的。
使用 new 来创建对象时,会单独为对象分配内存。之后,shared_ptr 的控制块会进行另一轮内存分配,以存储引用计数和其他管理信息。这意味着至少需要两次内存分配。

new 来创建对象额外开销

性能开销:
由于显式初始化需要两次内存分配,这会增加性能开销,特别是在需要频繁创建和销毁对象的场景中。
每次内存分配都需要调用操作系统的内存分配器,这会增加开销并导致性能下降。
内存溢出问题:
如果在高频率分配内存的情况下,由于系统的内存管理策略,可能会导致内存碎片的增加,进而提高内存溢出的风险。频繁的内存分配和释放会导致内存不可用的情况,从而引发 std::bad_alloc 异常。

make_shared:

auto ptr = std::make_shared<MyClass>(constructor_args);

make_shared会为对象的构造分配内存,并返回一个shared_ptr,同时在单个内存分配中管理对象和控制块(用于引用计数等信息)。

make_shared 的优点:

make_shared 的优点:

只进行一次内存分配,性能更优。
降低了内存碎片的可能性,减少了内存溢出的风险。
提供了更好的异常安全性,确保内存管理更加高效。

因此,在实际开发中,推荐使用 make_shared 来创建 shared_ptr,以提高性能和内存管理的安全性。

智能指针的实现?(share)

原理:智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函
数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程
序)结束时会自动被释放

➢ 智能指针(smart pointer)的通用实现技术是使用引用计数。智能指针类
将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象的指
针指向同一对象。
+1:
每次创建类的新对象时,初始化指针就将引用计数置为 1;当对象作为另
一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计
数;
-1:
对一个对象进行赋值:(如果引用计数为减至 0,则删除对象),并增加右
操作数所指对象的引用计数;(只能转移,不能拷贝,因此赋值运算会使左
操作数-1,右操作数+1)
调用析构函数时,析构函数减少引用计数(如果引用计数减至 0,则删除
基础对象)

注意:为了实现智能指针的效果,必须借助一个计数器,以便随时获知有多
少智能指针绑定在同一个对象上。显而易见,这个计数器不应该是智能指针
这个类的一部分。(指针内存和控制块分开创建)这是因为,如果计数器是智能指针类的一部分,那么每次增减计数器的值,都必须广播到每一个管理着目标对象的智能指针。这样做的代价太大了。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com