网站建设策目标,极简wordpress博客,qq企业邮箱怎么开通注册,宿迁网站建设报价一、智能指针 C 中的智能指针是一种用于管理动态分配的内存的对象#xff0c;它们可以自动进行内存管理#xff0c;避免内存泄漏和悬挂指针等问题。
1. 悬挂指针 悬挂指针#xff08;dangling pointer#xff09;是指在程序中仍然存在但已经不再指向有效内存地址的指针。悬…一、智能指针 C 中的智能指针是一种用于管理动态分配的内存的对象它们可以自动进行内存管理避免内存泄漏和悬挂指针等问题。
1. 悬挂指针 悬挂指针dangling pointer是指在程序中仍然存在但已经不再指向有效内存地址的指针。悬挂指针通常是由于以下情况引起的 1. 释放内存后未将指针置空 当使用 delete 或 free 等方法释放了指针指向的内存后如果未将指针置空即将指针设置为 nullptr 或 NULL则该指针仍然保留之前的内存地址成为悬挂指针。
int* ptr new int(42);
delete ptr;
// ptr现在成为悬挂指针它指向的内存已经被释放2. 超出作用域的引用 当指针指向的对象超出了其作用域例如指向了一个局部变量并且该对象的内存被释放那么指针就会成为悬挂指针。
int* danglingPtr;
{int value 42;danglingPtr value;
} // value的作用域结束danglingPtr成为悬挂指针3. 指向已经被销毁的对象 如果指针指向的对象在其生命周期内被销毁了那么指针就成为悬挂指针。
int* danglingPtr;
{std::unique_ptrint ptr std::make_uniqueint(42);danglingPtr ptr.get();
} // ptr超出作用域其指向的对象被销毁danglingPtr成为悬挂指针2.野指针 野指针是指指针指向的内存地址是随机的、未初始化的或者指向的内存区域未经过分配。野指针可能是在创建指针后没有给它赋值即未初始化指针或者是在释放内存后未将指针置空但继续使用该指针。 野指针指向的内存地址通常是随机的因此访问野指针可能会导致程序崩溃、数据损坏或安全漏洞。 悬挂指针是指针仍然保留着之前指向的地址但该地址已经不再有效而野指针是指针指向的地址是随机的或未经过初始化。在编程中应该尽量避免出现悬挂指针和野指针。
3.智能指针 C现代实用教程:智能指针_哔哩哔哩_bilibili 3.1 unique_ptr指针 3.1.1 特点 3.1.2 创建
栈上出作用域之后会自动调用析构函数
//stack
Cat c1(OK);
c1.cat_info();
{Cat c1(OK);c1.cat_info();
}
堆上需要手动delete释放不安全)
Cat *c_p1 new Cat(yy);
int *i_p1 new int(100);
c_p1-cat_info();
{int *i_p1 new int(200); // 重新声明但是是在局部作用域中与外部不同结果为100。需要delete两次Cat *c_p1 new Cat(yy_scope);c_p1-cat_info();delete c_p1; delete i_pi;
}delete c_p1; delete i_pi;
Cat *c_p1 new Cat(yy);
int *i_p1 new int(100);
c_p1-cat_info();
{i_p1 new int(200); // 不重新声明只是修改了值结果为200 只用delete一次Cat *c_p1 new Cat(yy_scope);c_p1-cat_info();delete c_p1; delete i_pi;
}delete c_p1; unique_ptr创建的三种方式
1. 通过原始指针创建
Cat *c_p2 new Cat(yz);
std::unique_ptrCat u_c_p2(c_p2};// 此时原始指针还能用需要进行销毁否则不满足独占指针要求,否则如下
//c_p2-cat_info();
//u_c_p2-cat_info();
//c_p2-set_cat_name(ok);
//u_c_p2-cat_info();// 销毁
c_p2 nullptr;
c_p2 nullptr;
u_c_p2-cat_info();
2. 使用new创建
std::unique_ptrCat u_c_p3{new Cat(dd)};
u_c_p3-cat_info();
u_c_p3-set_cat_name(oo);
u_c_p3-cat_info();3.使用std::make_unique
std::unique_ptrCat u_c_p4 make_uniqueCat();
u_c_p4-cat_info();
u_c_p4-set_cat_name(po);
u_c_p4-cat_info();4. 移动语义创建
可以通过使用移动语义将一个已有的 std::unique_ptr 赋值给另一个 std::unique_ptr。
#include memorystd::unique_ptrint u_c_p5 std::move(u_c_p4 );3.1.3 get()和常量类型
std::unique_ptrint u_i_p4 make_uniqueint(200);
cout int address u_i_p4 .get() endl; //get 获取原始指针或者地址
cout * u_i_p4endl; // 打印值
3.2 unique_ptr和函数调用 (资源的所属权问题 3.2.1 Passing by value 通过值传递
void do_with_cat_pass_value(std::unique_ptrCat c){c-cat_info();
}int main(){std::unique_ptrCat c1 make_uniqueCat(ff);// 1. 使用 std::move 转移资源的所有权给函数此时c1不再拥有资源的所有权了do_with_cat_pass_value(std::move(c1));// 2. 直接将参数传入make_unique ,将自动转换成movedo_with_cat_pass_value(std::make_uniqueCat()); // move}
3.2.2 Passing by reference 通过引用传递可以修改值
void do_with_cat_pass_ref(std::unique_ptrCat c){c-set_cat_name(oo);c-cat_info();c.reset(); // 释放先前所拥有的对象不再指向任何对象
}int main(){std::unique_ptrCat c2 make_uniqueCat(ff);// 不用使用move直接c2do_with_cat_pass_value(c2);}
void do_with_cat_pass_ref(const std::unique_ptrCat c){c-set_cat_name(oo);c-cat_info();// c.reset(); // 释放先前所拥有的对象不再指向任何对象 不能使用了
}int main(){std::unique_ptrCat c2 make_uniqueCat(ff);// 不用使用move直接c2do_with_cat_pass_value(c2);c2 -cat-info();} 3.3.3 Return by Value
std::unique_ptrCat get_unique_ptr(){std::unique_ptrCat p_dog std:: make_uniqueCat(Local cat);cout p_dog.get()endl;cout p_dog endl;return p_dog;
}
// 链式get_unique_ptr-cat_info(); p_dog.get(): 这个表达式返回指向 std::unique_ptr 管理的对象的原始指针。get() 函数是 std::unique_ptr 类的成员函数它返回一个指向被管理对象的原始指针。使用 get() 可以获取 std::unique_ptr 所拥有的对象的原始指针但是请注意这个原始指针不包含所有权信息因此需要谨慎使用特别是不要手动释放内存。 p_dog: 这个表达式返回的是指向 std::unique_ptr 对象本身的指针即指向 std::unique_ptr 对象的地址。 是取地址运算符它返回变量的地址。std::unique_ptr 是一个对象因此 p_dog 返回的是指向 std::unique_ptr 对象的指针
3.3 shared_ptr 计数指针/共享指针 在实际的 C 开发中我们经常会遇到诸如程序运行中突然崩溃、程序运行所用内存越来越多最终不得不重启等问题这些问题往往都是内存资源管理不当造成的。比如 有些内存资源已经被释放但指向它的指针并没有改变指向成为了野指针并且后续还在使用 有些内存资源已经被释放后期又试图再释放一次重复释放同一块内存会导致程序运行崩溃 没有及时释放不再使用的内存资源造成内存泄漏程序占用的内存资源越来越多。 智能指针shared_ptr 是存储动态创建对象的指针其主要功能是管理动态创建对象的销毁从而帮助彻底消除内存泄漏和悬空指针的问题。 shared_ptr的原理和特点 基本原理:就是记录对象被引用的次数当引用次数为 0 的时候也就是最后一个指向该对象的共享指针析构的时候共享指针的析构函数就把指向的内存区域释放掉。 特点:它所指向的资源具有共享性即多个shared_ptr可以指向同一份资源并在内部使用引用计数机制来实现这一点。 共享指针内存每个 shared_ptr 对象在内部指向两个内存位置 指向对象的指针 用于控制引用计数数据的指针。 1.当新的 shared_ptr 对象与指针关联时则在其构造函数中将与此指针关联的引用计数增加1。 2.当任何 shared_ptr 对象超出作用域时则在其析构函数中它将关联指针的引用计数减1。如果引用计数变为0则表示没有其他 shared_ptr 对象与此内存关联在这种情况下它使用delete函数删除该内存。 shared_ptr像普通指针一样使用可以将*和-与 shared_ptr 对象一起使用也可以像其他 shared_ptr 对象一样进行比较; 3.3.1 常量类型
int main(){std::shared_ptrint i_p_1 make_sharedint(10);// std::shared_ptrint i_p_2 make_sharedint{new int(10)};// copystd::shared_ptrint i_p_2 i_p_1;cout use cout : *i_p_1.use_cout() endl; // 1cout use cout : *i_p_2.use_cout() endl; // 1// change 两个指针指向同一个内存*i_p_2 30;cout : *i_p_1 endl; // 30cout : *i_p_2 endl; // 30// 将i_p_2置为nullptri_p_2nullptrcout use cout : *i_p_1.use_cout() endl; // 1cout use cout : *i_p_2.use_cout() endl; // 0// 将i_p_1置为nullptrstd::shared_ptrint i_p_3 i_p_1;i_p_1nullptrcout use cout : *i_p_1.use_cout() endl; // 0cout use cout : *i_p_2.use_cout() endl; // 2cout use cout : *i_p_3.use_cout() endl; // 2cout value: *i_p_1 endl;cout use cout : *i_p_1.use_cout() endl;return 0;
}
3.3.2 自定义类型
// 自定义类型
std::shared_ptrCat c_p_1 make_sharedCat();cout c_p_1 use cout : c_p_1.use_count() endl;std::shared_ptrCat c_p_2 c_p_1;
std::shared_ptrCat c_p_3 c_p_1;cout c_p_1 use cout : c_p_1.use_count() endl;
cout c_p_2 use cout : c_p_2 .use_count() endl;
cout c_p_3 use cout : c_p_3 .use_count() endl;
3.3.3 make_shared的构建方法 1.构造函数创建
1.shared_ptrT ptr;//ptr 的意义就相当于一个 NULL 指针
2.shared_ptrT ptr(new T());//从new操作符的返回值构造
3.shared_ptrT ptr2(ptr1); // 使用拷贝构造函数的方法会让引用计数加 1//shared_ptr 可以当作函数的参数传递或者当作函数的返回值返回这个时候其实也相当于使用拷贝构造函数。
4./*假设B是A的子类*/
shared_ptrB ptrb(new B());
shared_ptrA ptra( dynamic_pointer_castA(ptrb) );//从 shared_ptr 提供的类型转换 (cast) 函数的返回值构造
5./* shared_ptr 的“赋值”*/
shared_ptrT a(new T());
shared_ptrT b(new T());
a b; // 此后 a 原先所指的对象会被销毁b 所指的对象引用计数加 1//shared_ptr 也可以直接赋值但是必须是赋给相同类型的 shared_ptr 对象而不能是普通的 C 指针或 new 运算符的返回值。//当共享指针 a 被赋值成 b 的时候如果 a 原来是 NULL, 那么直接让 a 等于 b 并且让它们指向的东西的引用计数加 1;// 如果 a 原来也指向某些东西的时候如果 a 被赋值成 b, 那么原来 a 指向的东西的引用计数被减 1, 而新指向的对象的引用计数加 1。
6./*已定义的共享指针指向新的new对象————reset()*/
shared_ptrT ptr(new T());
ptr.reset(new T()); // 原来所指的对象会被销毁 2make_shared辅助函数创建
std::shared_ptrint foo std::make_sharedint (10);
3.3.4 shared_ptr 与函数 void cat_by_value( std::shared_ptrCat cat){cout cat use coout cat.use_cout() endl; // 2
}void cat_by_ref( std::shared_ptrCat cat){// cat.reset(new Cat()); // 先创建新对象将原先cat对象内容覆盖然后resetcout cat use coout cat.use_cout() endl; // 2
}std::shared_ptrCat get_shared_ptr(){std::shared_ptrCat cat_p std::make_sharedCat(dd);
}int main(){std::shared_ptrCat c1 make_sharedCat(dd);cat_by_value(c1);cout c1 use coout c1.use_cout() endl; // 1cat_by_ref(c1);std::shared_ptrCat c_p get_shared_ptr();get_shared_ptr-cat_info();
}
3.4 shared_ptr 和 unique_ptr转换 3.5 weak_ptr 弱引用指针 std::weak_ptr 是 C11 引入的一个智能指针类用于解决 std::shared_ptr 的循环引用问题。它是一个弱引用指针不会增加指向对象的引用计数也不会拥有对象的所有权因此不会影响对象的生命周期。
std::weak_ptr 主要用于解决以下两个问题 循环引用问题当两个或多个对象相互持有对方的 std::shared_ptr就会形成循环引用导致对象无法被正确释放从而产生内存泄漏。使用 std::weak_ptr 可以打破循环引用避免内存泄漏的发生。 避免悬挂指针当对象的 std::shared_ptr 被释放后指向该对象的 std::weak_ptr 仍然可以继续存在但是无法访问对象。因此使用 std::weak_ptr 可以避免悬挂指针的出现从而提高程序的稳定性。 使用 std::weak_ptr 需要配合 std::shared_ptr 使用通过 std::shared_ptr 对象的 weak_ptr 方法来创建 std::weak_ptr 对象。std::weak_ptr 可以通过 lock 方法获取一个有效的 std::shared_ptr 对象用于访问所指向的对象但是需要注意获取的 std::shared_ptr 可能为空需要进行有效性检查。 // 产生循环依赖问题 // 使用weak_ptr