指针这几个没用的判空,你做了吗?

指针这几个没用的判空,你做了吗?

好像智能指针出来后,裸指针变成了洪水猛兽,在项目里都敬而远之了,导致很多裸指针的知识点也就被束之高阁,但是我个人认为对于基础知识的深入了解才能走的更远,今天我也就斗胆抛砖引玉,梳理下裸指针的几个知识点,与大家共享。

1.new 完需要判空吗我之前写的代码、同事现在写的代码、读者群内部分同学也说,都会在new 后默认加一个指针是否为空的判断——指针为空时做一些容错处理。形如:

代码语言:javascript代码运行次数:0运行复制int* p = new int(42);

if (p == nullptr) {

// 处理内存分配失败的情况

}

int* p_array = new int[10];

if (p_array == nullptr) {

// 处理内存分配失败的情况

}

但是,new完的指针真的有必要进行判空吗?为了探究这个问题,查看对应的源码(以MSVC为例),源码实现如下:

代码语言:javascript代码运行次数:0运行复制_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 运算符的行为,是这样描述的:

代码语言:javascript代码运行次数:0运行复制Throws an exception of a type that would match a handler of

type std::bad_alloc on failure to allocate memory.

那么函数真的会抛出异常吗?如下的测试程序确实会抛出异常

代码语言:javascript代码运行次数:0运行复制int main() {

//编译器对单次分配过大内存会有编译报错,

//所以少量多次分配,避免编译报错

for (size_t i = 0; i <( 2<<30); i++)

{

auto p = new int[2<<10];

}

return 0;

}

问题:make_shared、make_unique的返回值需要判空吗?欢迎评论区讨论

2. 禁止new 的异常经过如上的分析可知,new 分配失败时会抛出异常,但是异常是不可控的,作为程序员我们总是希望程序是可控的,那如何禁止new 抛出异常呢? C++标准库提供了std::nothrow 关键字,它可以关闭new 分配失败时抛出异常的机制,转而返回空指针。可按如下的方式使用该关键字:

代码语言:javascript代码运行次数:0运行复制#include

int* p = new(std::nothrow) int(42);

if (p == nullptr) {

// 安全处理分配失败的情况

}

在MSVC中,std::nothrow版本的new操作符实现如下:

代码语言:javascript代码运行次数:0运行复制_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后,就不会抛出异常了,执行了返回值为空的逻辑,保证了程序的健壮性。

代码语言:javascript代码运行次数:0运行复制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

3. 指定内存上构建对象如上讲述的new操作符,都是通过operator new函数分配内存,然后调用对象的构造函数。一旦涉及到内存分配动作,便有可能出现内存分配失败的情况,无论是抛出异常还是返回空指针都不能构建出对象,都会影响预定的实现逻辑。为此,C++标准提供对应的解决办法,那就是使用placement new——在指定的内存上分配对象,而不是通过operator new函数分配内存。 具体的书写方式如下:

代码语言:javascript代码运行次数:0运行复制

char buffer[sizeof(int)];

int* p = new (buffer) int(123); // 在指定内存上构造对象

//some code

p->~int(); // 显式析构

注意:

预分配的内存必须足够大,以容纳对象的大小。在两个字节的内存上分配一个int对象是不合法的。new操作符不会分配内存,只负责调用构造函数。所以指针用完后,不能用delete操作符销毁对象,而是应该手动调用析构函数。预分配的内存需要手动释放,否则会导致内存泄漏。placement new 是构建内存池、自定义容器、或管理对象生命周期粒度精细化的重要技术,但也带来了较高的责任。其使用场景需极度谨慎,并确保对象构造与析构始终成对出现。4. delete 指针需要判空吗与new 完判空一样,好像delete指针时,大家也会默认加一个判空,避免空指针的问题,但是这真的有必要吗? 其实也是完全没有必要的,C++ 明确规定,对空指针使用 delete 操作是安全的:

代码语言:javascript代码运行次数:0运行复制int* p = nullptr;

delete p; // 无操作,不会导致崩溃

p=nullptr; // 一定要置空

我们知道了delete空指针是安全的,所以需要注意,delete完指针后,一定要将指针制空,避免出现double free的问题——多次删除同一非空指针(悬空指针)是有问题的

问题:空的shared_ptr、unique_ptr在reset前需要判空吗?

相关发现

2025广场舞鞋十大品牌排行榜,广场舞鞋哪个牌子好,就来窝豆品牌榜。
亚马逊变体代表什么
best365官网登录入口

亚马逊变体代表什么

🌼 07-12 🌻 6045
映客怎麼提現?映客提現教程
365bet体育手机

映客怎麼提現?映客提現教程

🌼 08-09 🌻 1043
檸怎么读
365bet体育手机

檸怎么读

🌼 09-14 🌻 7181