一、auto关键字
1.自动推导变量类型
2.auto
与decltype
的区别
auto
用于推导变量的类型,而decltype
用于推导表达式的类型。
3.auto
与范围for循环
std::vector<int> vec = {1, 2, 3, 4};
for (auto element : vec) {
std::cout << element << " ";
}
4.auto
的限制
auto
不能用于推导未初始化变量的类型;推导时必须有初始化表达式。
此外,auto
无法推导函数参数类型或返回类型的具体形式。
二、智能指针——用于自动管理动态分配内存的生命周期
1. std::unique_ptr
主要特点
- 独占所有权:
std::unique_ptr
保证一个对象只有一个所有者,避免了多个智能指针同时管理同一对象的情况。- 自动释放资源:当
std::unique_ptr
超出作用域时,它会自动释放它管理的对象,防止内存泄漏。- 不能复制:
std::unique_ptr
不允许复制构造和赋值操作,只能通过移动构造和移动赋值来转移所有权。
主要操作
创建和初始化:
std::unique_ptr<int> ptr = std::make_unique<int>(10); //std::make_unique 是推荐的创建方式,它会返回一个 std::unique_ptr,并且可以避免潜在的异常安全问题。
移动所有权:
std::unique_ptr<int> ptr1 = std::make_unique<int>(20); std::unique_ptr<int> ptr2 = std::move(ptr1); // ptr1 现在为空,ptr2 拥有所有权
访问和操作对象:
//使用 * 操作符来访问和修改 std::unique_ptr 管理的对象。 std::unique_ptr<int> ptr = std::make_unique<int>(30); *ptr = 40; // 修改值 int value = *ptr; // 访问值
释放资源:
std::unique_ptr<int> ptr = std::make_unique<int>(50); ptr.reset(); // 释放资源,将 ptr 置为空
实际应用示例
动态数组管理:
std::unique_ptr<int[]> arr = std::make_unique<int[]>(5); for (int i = 0; i < 5; ++i) { arr[i] = i * 10; }
封装资源管理:确保在对象超出作用域时自动释放资源。
class Resource { public: Resource() { /* allocate resources */ } ~Resource() { /* release resources */ } }; std::unique_ptr<Resource> res = std::make_unique<Resource>();
避免资源泄漏的工厂函数
std::unique_ptr<int> createResource() { return std::make_unique<int>(100); } void useResource() { std::unique_ptr<int> resource = createResource(); // 使用 resource }
2.shared_ptr
是一个引用计数智能指针,允许多个
shared_ptr
实例共享同一个对象的所有权。
它会自动管理对象的生命周期,当最后一个shared_ptr
离开作用域或被重置时,自动释放对象的内存。std::shared_ptr
提供了自动内存管理和共享所有权的功能,适用于需要多个所有者的场景。注意避免循环引用以防内存泄漏,适当使用std::weak_ptr
解决此问题。
主要特点
- 引用计数管理:
std::shared_ptr
使用一个引用计数来追踪有多少个shared_ptr
实例指向同一个对象。当引用计数变为零时,自动删除对象。- 线程安全:对引用计数的操作是线程安全的,但对象的管理仍需注意线程同步。
- 复制和赋值:可以复制和赋值
std::shared_ptr
,每个shared_ptr
都持有一个对同一对象的引用。
基本用法
创建和初始化
#include <iostream> #include <memory> int main() { std::shared_ptr<int> p1 = std::make_shared<int>(10); // 创建一个 shared_ptr std::cout << *p1 << std::endl; // 输出 10 }
复制和赋值
#include <iostream> #include <memory> int main() { std::shared_ptr<int> p1 = std::make_shared<int>(20); std::shared_ptr<int> p2 = p1; // p2 和 p1 共享所有权 std::cout << *p2 << std::endl; // 输出 20 }
使用
reset
方法#include <iostream> #include <memory> int main() { std::shared_ptr<int> p1 = std::make_shared<int>(30); p1.reset(); // 释放 p1 持有的对象 if (!p1) { std::cout << "p1 is null" << std::endl; } }
获取底层原始指针
原始指针是
shared_ptr
持有的实际对象的直接指针,但不管理对象的生命周期。使用原始指针时需小心,以避免在智能指针仍在管理对象的情况下误用或误删除该对象。#include <iostream> #include <memory> int main() { std::shared_ptr<int> p1 = std::make_shared<int>(40); int* rawPtr = p1.get(); // 获取底层原始指针 std::cout << *rawPtr << std::endl; // 输出 40 }
循环引用问题
#include <iostream> #include <memory> struct Node { std::shared_ptr<Node> next; ~Node() { std::cout << "Node destroyed" << std::endl; } }; int main() { std::shared_ptr<Node> a = std::make_shared<Node>(); std::shared_ptr<Node> b = std::make_shared<Node>(); a->next = b; b->next = a; // 形成循环引用 // 这里 a 和 b 都不会被销毁,因为它们互相引用 }
解决循环引用——使用弱指针
#include <iostream> #include <memory> struct Node { std::shared_ptr<Node> next; std::weak_ptr<Node> prev; // 使用 weak_ptr 解决循环引用 ~Node() { std::cout << "Node destroyed" << std::endl; } }; int main() { std::shared_ptr<Node> a = std::make_shared<Node>(); std::shared_ptr<Node> b = std::make_shared<Node>(); a->next = b; b->prev = a; // 使用 weak_ptr 打破循环引用 }
3.weak_ptr
与
std::shared_ptr
配合使用,用于解决循环引用问题。std::weak_ptr
不会影响引用计数,因此它不会阻止被指向对象的销毁。
主要特点
- 不影响引用计数:
std::weak_ptr
只观察一个std::shared_ptr
指向的对象,但不会增加对象的引用计数。它的存在不会影响对象的生命周期。- 可以转换为
std::shared_ptr
:使用weak_ptr
可以尝试获取一个shared_ptr
来访问对象,这样可以安全地检查对象是否仍然存在。- 打破循环引用:
std::weak_ptr
是避免std::shared_ptr
之间互相持有引用而导致内存泄漏的有效工具。
常见用法
创建和初始化
#include <iostream> #include <memory> int main() { std::shared_ptr<int> sp = std::make_shared<int>(10); std::weak_ptr<int> wp = sp; // wp 不会增加引用计数 std::cout << "shared_ptr use_count: " << sp.use_count() << std::endl; // 输出 1 }
转换为
shared_ptr
#include <iostream> #include <memory> int main() { std::shared_ptr<int> sp = std::make_shared<int>(20); std::weak_ptr<int> wp = sp; if (auto sp2 = wp.lock()) { // 尝试获取 shared_ptr std::cout << "Value: " << *sp2 << std::endl; // 输出 20 } else { std::cout << "The object has been deleted." << std::endl; } }
详细示例:缓存和观察者模式
#include <iostream> #include <memory> #include <vector> class Observer { public: virtual void update() = 0; }; class Subject { public: void addObserver(std::weak_ptr<Observer> observer) { observers.push_back(observer); } void notify() { for (auto it = observers.begin(); it != observers.end(); ) { if (auto observer = it->lock()) { observer->update(); // 调用观察者的 update 方法 ++it; } else { it = observers.erase(it); // 移除过期的观察者 } } } private: std::vector<std::weak_ptr<Observer>> observers; }; class ConcreteObserver : public Observer { public: void update() override { std::cout << "ConcreteObserver updated!" << std::endl; } }; int main() { std::shared_ptr<Subject> subject = std::make_shared<Subject>(); { std::shared_ptr<ConcreteObserver> observer1 = std::make_shared<ConcreteObserver>(); subject->addObserver(observer1); subject->notify(); // 触发通知 observer1.reset(); // 销毁 observer1 subject->notify(); // 不再有有效的观察者 } subject->notify(); // 此时 observers 列表应该为空 }