好像智能指针出来后,裸指针变成了洪水猛兽,在项目里都敬而远之了,导致很多裸指针的知识点也就被束之高阁,但是我个人认为对于基础知识的深入了解才能走的更远,今天我也就斗胆抛砖引玉,梳理下裸指针的几个知识点,与大家共享。
new
完需要判空吗我之前写的代码、同事现在写的代码、读者群内部分同学也说,都会在new
后默认加一个指针是否为空的判断——指针为空时做一些容错处理。形如:
int* p = new int(42);
if (p == nullptr) {
// 处理内存分配失败的情况
}
int* p_array = new int[10];
if (p_array == nullptr) {
// 处理内存分配失败的情况
}
但是,new
完的指针真的有必要进行判空吗?为了探究这个问题,查看对应的源码(以MSVC为例),源码实现如下:
_VCRT_EXPORT_STD _NODISCARD _Ret_notnull_ _Post_writable_byte_size_(_Size) _VCRT_ALLOCATOR
void* __CRTDECL operator new(
size_t _Size
);
_VCRT_EXPORT_STD _NODISCARD _Ret_notnull_ _Post_writable_byte_size_(_Size) _VCRT_ALLOCATOR
void* __CRTDECL operator new[](
size_t _Size
);
可以看到,new
运算符的实现中加入了修饰符_Ret_notnull_
,它的作用是告诉编译器,该函数返回的指针不会为空。
如果说如上所示的源码还是比较片面,但是C++标准对于new
运算符的行为,是这样描述的:
Throws an exception of a type that would match a handler of
type std::bad_alloc on failure to allocate memory.
那么函数真的会抛出异常吗?如下的测试程序确实会抛出异常
int main() {
//编译器对单次分配过大内存会有编译报错,
//所以少量多次分配,避免编译报错
for (size_t i = 0; i <( 2<<30); i++)
{
auto p = new int[2<<10];
}
return 0;
}
问题:make_shared、make_unique的返回值需要判空吗?欢迎评论区讨论
new
的异常经过如上的分析可知,new
分配失败时会抛出异常,但是异常是不可控的,作为程序员我们总是希望程序是可控的,那如何禁止new
抛出异常呢? C++标准库提供了std::nothrow
关键字,它可以关闭new
分配失败时抛出异常的机制,转而返回空指针。可按如下的方式使用该关键字:
#include <new>
int* p = new(std::nothrow) int(42);
if (p == nullptr) {
// 安全处理分配失败的情况
}
在MSVC中,std::nothrow
版本的new
操作符实现如下:
_VCRT_EXPORT_STD _NODISCARD _Ret_maybenull_ _Success_(return != NULL) _Post_writable_byte_size_(_Size) _VCRT_ALLOCATOR
void* __CRTDECL operator new(
size_t _Size,
::std::nothrow_t const&
) noexcept;
_VCRT_EXPORT_STD _NODISCARD _Ret_maybenull_ _Success_(return != NULL) _Post_writable_byte_size_(_Size) _VCRT_ALLOCATOR
void* __CRTDECL operator new[](
size_t _Size,
::std::nothrow_t const&
) noexcept;
可以看到,std::nothrow
版本的new
操作符实现中加入了修饰符_Ret_maybenull_
,它的作用是告诉编译器,该函数返回的指针可能为空。
之前抛出异常的代码使用std::nothrow
后,就不会抛出异常了,执行了返回值为空的逻辑,保证了程序的健壮性。
int main() {
for (size_t i = 0; i <( 2<<30); i++)
{
auto p = new(std::nothrow) int[2<<10];
if (p == nullptr) {
printf("new failed\n");
}
}
return 0;
}
//输出:new failed
如上讲述的new
操作符,都是通过operator new
函数分配内存,然后调用对象的构造函数。一旦涉及到内存分配动作,便有可能出现内存分配失败的情况,无论是抛出异常还是返回空指针都不能构建出对象,都会影响预定的实现逻辑。为此,C++标准提供对应的解决办法,那就是使用placement new
——在指定的内存上分配对象,而不是通过operator new
函数分配内存。 具体的书写方式如下:
char buffer[sizeof(int)];
int* p = new (buffer) int(123); // 在指定内存上构造对象
//some code
p->~int(); // 显式析构
注意:
new
操作符不会分配内存,只负责调用构造函数。所以指针用完后,不能用delete
操作符销毁对象,而是应该手动调用析构函数。delete
指针需要判空吗与new
完判空一样,好像delete
指针时,大家也会默认加一个判空,避免空指针的问题,但是这真的有必要吗? 其实也是完全没有必要的,C++ 明确规定,对空指针使用 delete
操作是安全的:
int* p = nullptr;
delete p; // 无操作,不会导致崩溃
p=nullptr; // 一定要置空
我们知道了delete空指针是安全的,所以需要注意,delete完指针后,一定要将指针制空,避免出现double free
的问题——多次删除同一非空指针(悬空指针)是有问题的
问题:空的shared_ptr、unique_ptr在reset前需要判空吗?
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有