c++nullptr(空指针常量)、constexpr(常量表达式)

简介: c++nullptr(空指针常量)、constexpr(常量表达式)

总述

   又来更新了,今天带来的是nullptr空指针常量、constexpr(常量表达式)C++的两个用法。Result result_fun = nullptr;constexpr static uint32_t try_times = 100;这是两个在工作中常用的C++操作,但是你知道nullptr和constexpr由来以及它们的更多用法吗?

   下面听我一一道来。

作者:良知犹存

转载授权以及围观:欢迎添加微信公众号:羽林君


一、nullptr

C++11要引入nullptr?它与NULL相比又有什么不同呢?  

加上nullptr,我们就有下面三种方法来获取一个“空指针”:

int *p1 = NULL; 
int *p2 = 0;
int *p3 = nullptr;

    空指针是不会指向有效数据的指针。以前,C++在源代码中使用0表示这种指针,但内部表示可能不同。这带来了一些问题,因为这使得0既可以表示指针常量,又可表示整形常量。

   所以C++11新增了关键字nullptr,用于表示空指针;它是指针类型,不能转化为整形类型。为向后兼容,C++11仍然允许0来表示空指针,因此表达式nullptr=0为true,但使用nullptr而不是0提供了更高的类型安全。例如,可将0传递给接受int参数的函数,但如果您试图将nullptr传递给这样的函数,编译器将此视为错误。因此,出于清晰和安全的考虑,请使用nullptr——如果你的编译器支持它。

空指针常量nullptr注意事项:

(1) 可以隐式转换为任意类型的指针或者指向任何成员。

(2) 不能隐式转换为整型,可以隐式转换为bool类型代表false。

(3) 可以和除了整型和bool型的其他类型比较。


二、constexptr

   常量表达式(const expression)是指值不会改变并且在编译过程就能得到计算结果的表达式。显示,字面值属于常量表达式,用表达式初始化的const对象也是常量表达式。后面会提到,C++语言中有几种情况是要用到常量表达式的。

   一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定,例如

const int max_num = 20;           // max_num是常量表达式
const int limit = max_num + 1;    // limit 是常量表达式
int staff_size = 2;               // staff_size 不是常量表达式,因为staff_size没有用const修饰
const int sz = get_size();        // sz 不是常量表达式,虽然sz是个常量,但它的值在运行时才能确定

   尽管staff_size 的初始值是个字面值常量,但由于它的数据类型只是一个普通的int  而非const int,所以它不属于常量表达式。另一方面,尽管sz是一个常量,但它的具体值要等到运行时才能获取到,所以也不是常量表达式。

const变量:

在一个复杂系统中,很难分辨一个初始值到底是不是常量表达式。当然也可以定义一个const变量并把它的初始值设为我们认为的某个常量表达式,但在实际使用时,尽管要求如此却常常发现初始值并非常量表达式的情况。可以这么说,在此种情况下,对象的定义和使用根本就是两回事。

C++11新标准规定,允许将变量声明为constexpr类型以便编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量值表达式初始化:

constexpr int mf = 20;    // mf是常量表达式
constexpr int limit = mf + 1;  // limit 是常量表达式
​constexpr int sz = get_sz();  // 只有当 get_sz() 是一个 constexpr 函数时才是一条正确的声明语句

字面值类型:    

常量表达式的值需要在编译时就得到计算,因此对声明constexpr时用到的类型必须有所限制。因为这些类型一般比较简单,值也显而易见、容易得到,就把它们称为“字面值类型”(interal type)。    到目前为止接触过的数据类型中,算术类型、引用和指针都属于字面值类型。自定义类 Sales_item、IO库、string 类型则不属于字面值类型,也就不能被定义为constexpr。    尽管指针和引用都能定义成constexpr,但它们的初始值却受到严格限制。一个constexpr指针的初始值必须是nullptr或者是0,或者是存储于某个固定地址中的对象。而函数体内定义的变量一般来说并非存放到固定地址中,因此constexpr之指针不能只想这样的变量。(对于程序执行中各个变量分配的地址不明白的,可以点击链接看我之前的一篇文章)

   相反的,定义于所有函数体之外的对象其地址固定不变,能用来初始化constexpr指针。

指针和constexper:

   必须明确一点,在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关:

const int *p = nullptr;    // p 是一个指向整型常量的指针
constexpr int *q = nullptr;  // q 是一个指向整数的常量指针,constexpr 仅对指针有效

   p和q的类型相差甚远,p是一个指向常量的指针,而q是一个常量指针,其中关键在于constexpr把它所定义的对象置为了顶层 const类似于int * const  p = nullptr;    与其他常量指针类似,constexpr指针既可以指向常量也可以指向一个非常量

constexpr int *np  = nullptr;//np是一个指向整数的常量指针,其值为空
int j= 0;
constexpr int i=42;//i的类型是整型常量

i和j都必须定义在函数体之外

constexpr const int *p = &i;//p是指针常量,指向整型常量i
constexpr int *p1 = &j; //p1是常量指针,指向整数j

constexpr函数:

   尽管不能使用普通函数作为constexpr变量的初始值,但新标准允许定义一种特殊的constexpr函数。这种函数应该足够简单以使得编译时就可以计算其结果,这样就能用constexpr函数去初始化constexpr变量了。

constexpr函数的限制

(1) 函数中只能有一个return语句(有极少特例)

(2) 只能调用其他constexpr函数。

(3) 只能使用全局constexpr变量。

(4) 递归并不受限制,但只允许一个返回语句,可以使用三元运算符。

(5) constexpr函数,只允许包含一行可执行代码。但允许包含typedefs、using declaration&&directives、静态断言等。

(6) 一个声明为constexpr的函数同样可以在运行时被调用,即当这个函数的参数是非常量的时候。

   constexpr函数是只能用于常量表达式的函数。不同于一般函数,constexpr 函数的返回值类型及所有形参的类型都是字面值类型(算术类型,引用,指针等属于字面值类型),而且函数体中有且只有一条 return 语句。为了能在编译过程中随之展开,constexpr函数被隐式地指定为内联函数。constexpr 函数的返回值在编译时就能被确定。

我们允许 constexpr 函数不一定返回常量表达式,但是我们认为: 如果 arg 为常量表达式,则 scale(arg)也是常量表达式

#include <iostream>
constexpr size_t scale(size_t cnt) {
return 2 * cnt;
}
int main () {
constexpr size_t sz;
constexpr size_t s = 3;
        sz = scale(s);
std::cout << sz << std::endl;
return 0;
}

可见,constexpr 函数不一定返回常量表达式,但是在 scale(3) 可以在编译时确定。

这就是我分享的c++的nullptr和constexpr,如果大家有什么更好的思路,欢迎分享交流哈。

参考:C++Primer Plus (第5版)、C++Primer Plus (第6版)

https://blog.csdn.net/JinhuCheng/article/details/87545143?utm_medium=distribute.pc_relevant.none-task-blog-title-1&spm=1001.2101.3001.4242

https://blog.csdn.net/woxiaohahaa/article/details/78512576


目录
相关文章
|
1月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
58 12
|
5月前
|
存储 程序员 C++
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
183 1
|
6月前
|
算法 编译器 C++
【C++11】lambda表达式
C++11 引入了 Lambda 表达式,这是一种定义匿名函数的方式,极大提升了代码的简洁性和可维护性。本文详细介绍了 Lambda 表达式的语法、捕获机制及应用场景,包括在标准算法、排序和事件回调中的使用,以及高级特性如捕获 `this` 指针和可变 Lambda 表达式。通过这些内容,读者可以全面掌握 Lambda 表达式,提升 C++ 编程技能。
341 3
|
6月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
410 4
|
7月前
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
7月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
103 2
|
7月前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
213 1
|
7月前
|
存储 C++ 索引
C++函数指针详解
【10月更文挑战第3天】本文介绍了C++中的函数指针概念、定义与应用。函数指针是一种指向函数的特殊指针,其类型取决于函数的返回值与参数类型。定义函数指针需指定返回类型和参数列表,如 `int (*funcPtr)(int, int);`。通过赋值函数名给指针,即可调用该函数,支持两种调用格式:`(*funcPtr)(参数)` 和 `funcPtr(参数)`。函数指针还可作为参数传递给其他函数,增强程序灵活性。此外,也可创建函数指针数组,存储多个函数指针。
166 6
|
7月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
126 1
|
8月前
|
编译器 C++
【C++核心】指针和引用案例详解
这篇文章详细讲解了C++中指针和引用的概念、使用场景和操作技巧,包括指针的定义、指针与数组、指针与函数的关系,以及引用的基本使用、注意事项和作为函数参数和返回值的用法。
117 3