智能指针
-
智能指针:
- C++11引入了四种智能指针:
auto_ptr
(已弃用)、unique_ptr
、shared_ptr
和weak_ptr
。智能指针可以更有效地管理堆内存,并避免常见的内存泄漏问题。
- C++11引入了四种智能指针:
-
shared_ptr: 自定义删除器。
shared_ptr
使用引用计数来管理它指向的对象的生命周期。多个shared_ptr
实例可以指向同一个对象,只有最后一个shared_ptr
被销毁时,该对象才会被销毁。- 使用shared_ptr要注意的问题
- 不要用一个原始指针初始化多个shared_ptr
- 不要在函数实参中创建shared_ptr,因为编译器差异,没有明确指定参数顺序。
function(shared_ptr<int>(new int), g()); //有缺陷
- 通过shared_from_this()返回this指针。 解决引发两次析构问题,底层就是使用weak_ptr
class A: public std::enable_shared_from_this<A> shared_ptr<A>GetSelf() { //return shared_ptr<A>(this); // 不要这么做 return shared_from_this(); // 正确方式 }
- 避免循环引用
A:shared_ptr B:shared_ptr 问题:A->B B->A 这种情况会导致内存泄漏,析构只进行-1次引用计数。 解决:A:shared_ptr B:weak_ptr 即可
-
unique_ptr:
unique_ptr
是一种独占式的智能指针,不允许复制,但可以移动。它适用于管理应该有单一所有者的资源。- 讨论了
unique_ptr
和shared_ptr
之间的差异,如数组支持和自定义删除器。 - 使用shared_ptr要注意的问题
- 初始化灾难。new T会造成重复类型声明。代码可读性差
unique_ptr<T> my_ptr(new T); unique_ptr<T> my_other_ptr = my_ptr; // 报错,不能复制 // 方法1:重复类型(不推荐) std::unique_ptr<ComplexObject> obj1( new ComplexObject(10, "example") ); // 方法2:避免重复(推荐) auto obj2 = std::make_unique<ComplexObject>(10, "example"); // 重复类型声明的潜在问题: // - 编译器需要多次处理相同类型 // - 增加编译时间 // - 可能导致目标代码膨胀
- 数组支持。
std::unique_ptr<int []> ptr(new int[10]); ptr[9] = 9; std::shared_ptr<int []> ptr2(new int[10]); // 这个是不合法的
-
删除器区别。需要指定删除器类型
std::shared_ptr<int> ptr3(new int(1), [](int *p){delete p;}); // 正确 std::unique_ptr<int> ptr4(new int(1), [](int *p){delete p;}); // 错误 // 方法2:自定义更复杂的分配 auto customDeleter = [](int* p) { // 可以添加额外的清理逻辑 delete[] p; }; auto ptr2 = std::unique_ptr<int[], decltype(customDeleter)>( new int[5](), customDeleter );
- 初始化灾难。new T会造成重复类型声明。代码可读性差
-
weak_ptr:
weak_ptr
是一种非拥有式的智能指针,用于观察由shared_ptr
管理的对象的生命周期。它用于打破循环引用,防止内存泄漏。涵盖了基本用法、将this
指针返回为weak_ptr
以及解决循环引用问题。-
举个例子,监视某内存的释放。
-
std::weak_ptr<int> gw; void f() { if(gw.expired()) { cout << "gw无效,资源已释放"; } else { auto spt = gw.lock(); cout << "gw有效, *spt = " << *spt << endl; } } int main() { { auto sp = atd::make_shared<int>(42); gw = sp; f(); } f(); return 0; }
-
-
weak_ptr使用注意事项
-
weak_ptr在使用前需要检查合法性。在使用wp前需要调用wp.expired()函数判断一下
-
-
智能指针安全性:
- 智能指针通常是安全的,但在多线程访问同一个
shared_ptr
对象时需要考虑线程安全性。 - 当每个线程有自己的
shared_ptr
实例时,只要底层数据相同就是安全的。
- 智能指针通常是安全的,但在多线程访问同一个
右值引用和移动语义
关键点总结:
- 左值:可取地址的持久对象
- 右值:临时对象,不可取地址。 比如临时变量,字面量常量
右值的关键特征
- 不可取地址
- 只能出现在赋值表达式右侧
- 没有持久的内存位置
- 可以被移动,但不能被修改
- 通常是临时的、短暂的对象
- 右值引用可以延长临时对象生命周期
- std::move 无条件地将对象转换为右值
- 移动语义避免不必要的深拷贝
- 万能引用可以接受左值和右值
template<typename T> // 引用折叠规则 // T& & -> T& // T&& & -> T& // T& && -> T& // T&& && -> T&& void forwardingFunc(T&& arg) { // arg 可能是左值引用或右值引用 someFunc(std::forward<T>(arg)); } void testUniversalRef() { int x = 10; forwardingFunc(x); // T 推导为 int& forwardingFunc(10); // T 推导为 int }
C++11 在性能上做了很大的改进,最大程度减少了内存移动和复制,通过右值引用、 forward、 emplace 和一些无序容器我们可以大幅度改进程序性能。 右值引用仅仅是通过改变资源的所有者来避免内存的拷贝,能大幅度提高性能。 forward 能根据参数的实际类型转发给正确的函数。 emplace 系列函数通过直接构造对象的方式避免了内存的拷贝和移动。 无序容器在插入元素时不排序,提高了插入效率,不过对于自定义 key 时需要提供 hash 函数和比 较函数
匿名函数lambda
迭代器
使用一个 iterator 对象来指向一个可以修改的容器元素,使用一个 const_iterator 对象来指向一个不能 修改 的容器元素。
每种容器所支持的迭代器类型决定了这种容器是否可以在指定的 STL 算 法中使用。
const只能读,reverser是反向。正常使用都是iterator。
详细了解
C++ 参考手册 - cppreference.com
C++面试常见题目7_STL之map与unordered_map(红黑树VS哈希表)_map 和unordermap区别面试-CSDN博客
学习资料分享
0voice · GitHub