c++新特性(智能指针)
·
引言:
C++智能指针是一种封装原始指针的一种模板类,用于自动管理动态内存(堆内存),避免因为手动管理内存二次释放的问题。头文件<memory>.主要有三种智能指针unique_ptr、shared_ptr和weak_ptr。
unique_ptr:(独占所有权的智能指针)
确保同一时间只有一个智能指针拥有对动态资源的所有权,不允许复制(但允许移动),当它被销毁(如超出作用域)时,会自动释放所指向的资源。
特点:
- 独占性:同一对象只能被一个
unique_ptr拥有。 - 轻量级:性能接近原始指针,无额外引用计数开销。
- 不可复制,但可通过
std::move()转移所有权。
#include<iostream>
#include<memory>
using namespace std;
int main() {
unique_ptr<int>up1(new int(10));
/*不可复制性
* unique_ptr<int> up3(up1);
* up3=up1;体现了独占性其他占不了
* 编译器会报错,不能将一个unique_ptr对象赋值给另一个unique_ptr对象,因为unique_ptr对象是不能被复制的。
*/
cout<<*up1<<endl;
unique_ptr<int>up2;
up2=move(up1);//但可以移动赋值
if (up1 == nullptr) {
cout << "up1被释放了" << endl;
}
return 0;
}
shared_ptr:(共享所有权的智能指针)
shared_ptr 允许多个智能指针共享对同一动态资源的所有权,通过引用计数(reference count)跟踪所有者数量:
- 当新的
shared_ptr指向同一资源时,引用计数 + 1。 - 当
shared_ptr被销毁或指向其他资源时,引用计数 - 1。 - 当引用计数变为 0 时,自动释放资源。
特点:
- 共享性:多个
shared_ptr可指向同一资源。 - 引用计数:通过计数管理资源生命周期,有一定性能开销。
- 可复制(复制时引用计数 + 1)。
#include<iostream>
#include<memory>
using namespace std;
int main() {
shared_ptr<int>sp1;
int a = 10;
sp1 = make_shared<int>(a);
cout << *sp1 << endl;
cout << sp1.use_count() << endl;//引用计数1
shared_ptr<int>sp2(sp1);
cout << sp2.use_count() << endl;//引用计数2
sp2.reset();
cout << sp1.use_count() << endl;//引用计数1
return 0;
}
问题:
循环引用问题:两个shared_ptr互相指向对方,导致引用计数永远不为 0,资源无法释放。
// 循环引用示例(错误)
struct A {
std::shared_ptr<B> b_ptr;
};
struct B {
std::shared_ptr<A> a_ptr;
};
int main() {
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b_ptr = b; // a持有b
b->a_ptr = a; // b持有a
// 离开作用域时,a和b的引用计数均为1(互相引用),资源泄漏
return 0;
}
weak_ptr:(解决循环引用的 “弱引用)
weak_ptr 是一种不拥有资源所有权的智能指针,它指向shared_ptr管理的资源,但不增加引用计数,主要用于解决shared_ptr的循环引用问题。
特点:
- 弱引用:不影响资源的引用计数,不拥有资源所有权。
- 需通过
lock()方法获取shared_ptr才能访问资源(确保资源未被释放)。 - 可通过
expired()检查资源是否已被释放。
#include <memory>
struct A {
std::weak_ptr<B> b_ptr; // 用weak_ptr替代shared_ptr
};
struct B {
std::weak_ptr<A> a_ptr; // 用weak_ptr替代shared_ptr
};
int main() {
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b_ptr = b; // 弱引用,不增加计数
b->a_ptr = a; // 弱引用,不增加计数
// 离开作用域时,a和b的引用计数均为0,资源正常释放
return 0;
}
访问资源:
// 通过weak_ptr访问资源
auto a = std::make_shared<int>(30);
std::weak_ptr<int> weak_a = a;
// 检查资源是否有效
if (!weak_a.expired()) {
// 获取shared_ptr以访问资源
std::shared_ptr<int> shared_a = weak_a.lock();
std::cout << *shared_a << std::endl; // 输出:30
}
nullptr和constexpr关键字:
nullptr关键字:
| 对比维度 | null | nullptr |
|---|---|---|
| 本质定义 | 通常是宏定义(#define NULL 0 或 (void*)0),本质是整数 0 或空指针常量的兼容表示。 | C++11 引入的关键字,是专门表示 “空指针” 的常量,类型为 nullptr_t。 |
| 类型 | 取决于实现:多数情况下是 int 类型(值为 0),部分场景下可能被定义为 (void*)0(空指针)。 | 专属类型 nullptr_t(C++ 新增的基础类型)。 |
| 引入版本 | 源自 C 语言,在早期 C++ 中兼容使用。 | C++11 及以后版本正式引入。 |
| 隐式转换 | 可隐式转换为任何整数类型(如 int、long)和任何指针类型,存在类型模糊问题。 | 仅可隐式转换为任何指针类型(包括对象指针、成员指针)和 bool 类型(转换为 false),不可转换为整数类型(无歧义)。 |
| 函数重载匹配 | 若存在 void f(int) 和 void f(int*) 重载,传递 null 会匹配 f(int)(因为 null 本质是 0),不符合 “空指针” 的预期。 | 传递 nullptr 会优先匹配指针参数的重载(如 f(int*)),符合空指针的语义。 |
| 模板适用性 | 作为模板参数时,会被推导为 int 类型,可能导致模板逻辑错误(如期望指针类型时)。 | 作为模板参数时,类型明确为 nullptr_t,可正确推导为指针相关逻辑,无歧义。 |
| 类型安全性 | 低:因可转换为整数,可能意外匹配非指针类型的函数 / 操作,导致隐蔽错误。 | 高:仅与指针类型兼容,避免了整数和指针之间的混淆,减少类型错误。 |
| 兼容性 | 兼容所有 C++ 版本(包括旧标准),但依赖宏定义的实现。 | 仅兼容 C++11 及以上版本,旧编译器(如 C++03)不支持。 |
constexpr关键字:
| 对比维度 | 详细说明 |
|---|---|
| 核心定义 | C++11 引入的关键字,用于声明可在编译时计算的常量表达式,将计算从运行时提前到编译时,兼顾性能优化与类型安全。 |
| 可修饰对象 | 1. 变量:必须用编译期可求值的表达式初始化,本质是 “编译时常量”(一定是 const 的);2. 函数:返回值可在编译时计算,C++14 后支持循环、分支等复杂逻辑;3. 构造函数:允许编译时创建自定义类型对象(需配合 constexpr 成员函数)。 |
| 修饰变量规则 | - 初始化表达式必须是常量表达式(如字面量、constexpr 函数结果);- 声明时必须初始化(不可延后赋值);- 类型需为 “字面类型”(如 int、double、带 constexpr 构造函数的自定义类)。 |
| 修饰函数规则 | - C++11:函数体仅允许 1 条 return 语句,无循环 / 分支,参数 / 返回值需为字面类型;- C++14+:允许循环、分支、局部变量,仅需保证 “传入常量表达式时能编译期求值”;- 调用时:若参数是常量表达式,结果编译期计算;若参数是运行时变量,结果运行时计算(双兼容)。 |
| 修饰构造函数规则 | - 构造函数体需为空(初始化逻辑通过初始化列表完成);- 成员变量需用常量表达式初始化;- 实例化对象时,若参数是常量表达式,对象在编译时创建。 |
与 const 的核心区别 | - const:运行时常量(初始化后不可改,但值可能运行时确定,如 const int x = rand(););- constexpr:编译时常量(值必须编译时确定,且一定是 const 的);- 示例:const int a = 10;(可能运行时确定) vs constexpr int b = 10;(编译时确定)。 |
| 关键特性 | 1. 双场景兼容:支持编译时计算(参数为常量表达式)和运行时计算(参数为变量);2. 类型安全:编译时计算可提前暴露错误(如数组越界);3. 扩展常量场景:可作为数组大小、模板参数、case 标签等 “编译期必需常量” 的来源。 |
| 典型使用场景 | 1. 定义编译期常量(如 constexpr int MAX_SIZE = 1024;);2. 优化简单计算(如 constexpr int factorial(int n) 编译时求阶乘);3. 编译时创建对象(如 constexpr Point p(3,4););4. 配合 constexpr if 实现编译期分支(C++17+);5. 作为模板参数(如 template<constexpr int N> class Arr {};)。 |
| 版本支持 | - C++11:基础功能(变量、简单函数、构造函数);- C++14:放宽函数限制(循环、分支、局部变量);- C++17:新增 constexpr if、constexpr lambda;- C++20:支持 constexpr 容器(如 std::vector、std::string)。 |
| 注意事项 | 1. 若 constexpr 函数传入运行时变量,其行为等同于普通函数(无编译时优化);2. 自定义类型需同时满足 “constexpr 构造函数 + constexpr 成员函数”,才能编译时使用;3. 避免过度使用复杂 constexpr 逻辑(可能增加编译时间)。 |
总结:
智能指针自动管理堆内存,一种是unique_ptr独享性内存,
shared_ptr共享内存
weak_ptr解决共享内存的循环引用,解决这个问题
更多推荐



所有评论(0)