C++ 中 const 和 constexpr 关键字解析:常量、函数和指针

本文涉及的产品
性能测试 PTS,5000VUM额度
可观测可视化 Grafana 版,10个用户账号 1个月
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 很多 C++ 的初学者看到 const 这个关键字的第一反应都是一头雾水,主要是因为 const可 以出现在很多的位置,以及后面加入的 constexpr 更是常常感到困惑,今天就为大家一一解释出现它们的含义和以及作用

很多 C++ 的初学者看到 const 这个关键字的第一反应都是一头雾水,主要是因为 const可 以出现在很多的位置,以及后面加入的 constexpr 更是常常感到困惑,今天就为大家一一解释出现它们的含义和以及作用

const关键字

const修饰变量

这是最基本的一种用法,顾名思义,就是将该变量修饰为常量,从而不可以修改。很多的全局变量都是通过常量来进行修饰,需要注意的是,使用const关键字修饰的变量需要立刻初始化

// 修饰局部变量,全局变量,成员变量
const int a = 2;
// 错误,表达式必须是可修改的左值,意思就是a是个常量,无法修改
a = 3;  

// 还有人习惯这种写法,作用是一样的,看个人喜好即可
int const b = 22;

// 修饰函数参数
void test(const int num) {
   
    // 错误,表达式必须是可修改的左值,意思就是参数num是个常量,无法修改
    num = 3;  
}
AI 代码解读

修饰指针

虽然指针也是一种变量,不过当const与指针出现在一起的时候,位置的不同会发生不同的作用,所以单独拎出来讲

// 第一种情况:指针常量
int a = 2;
// const作用:使其无法通过指针来修改变量
const int *p = &a;  
// 错误,表达式必须是可修改的左值
*p = 3;  
// 正确
a = 4; 
// 4
cout << *p << endl;  
// 同样地,有人习惯这种写法,作用是一样的,看个人喜好即可
int const *p2 = &a;

// 第二种情况:常量指针
int a = 2;
// const作用:使指针p无法指向其他变量
int* const p = &a;  
int b = 3;
// 错误,表达式必须是可修改的左值
p = &b;
AI 代码解读

修饰函数

const用于修饰函数也是最困惑的地方,主要原因在于它可以出现在不同的地方,并且每一个都有不同的含义。接下来为一一为大家解释

// 修饰函数返回值,这种用法毫无意义,它的作用相当于将返回值修饰为了常量,但是返回值是一个将亡值,在返回之后要么赋值给了其他的变量,然后其他变量可以继续修改,要么就随着离开作用域而被释放内存。所以通常不会这么使用。
const int getNum() {
   
    return 3;
}

// 修饰成员函数,通常加在成员函数的末尾,作用声明该成员函数为只读函数,即无法修改任何成员变量的值
class Student {
   
   public:
    void test() const {
   
        // 错误,表达式必须是可修改的左值,因为member是成员变量,而test函数被const修饰过后无法修改成员变量
        member = 3;  
        int b = 3;
        // 正确
        b = 4;  
    }

   private:
    int member = 2;
};
AI 代码解读

const引用

这是const最常用的一种方式,通常用于函数的参数列表中,因为我们知道在C++中函数参数有3中传递方式,分别是值传递指针传递(或者叫地址传递),引用传递,前两种在传递时都会发成拷贝行为

指针本身也是一个变量,在32位操作系统下占用4个字节,64位系统占用8个字节,虽然的拷贝成本会低一点,但是在大量的调用过程中也比较可观

所以通常我们采用传递引用的方式,因为引用只是变量的一个别名,不占用内存,所以不会发生拷贝行为。但是引用传递有一个问题,那就是形参可以改变实参的值。所以为了避免意外修改导致实参的值发生改,通常会采用const加上引用的方式传递参数

void test(const Student &s) {
   
    ...
}
AI 代码解读

constexpr关键字

constexprC++11中引入的一个关键字,它的作用主要是用来修饰一些函数和变量,使其成为常量表达式,从而在编译器就可以进行计算,进一步提高程序运行期的效率

  • 常量表达式:指的是有一个或多个常量组成的表达式,在实际开发中经常会接触到常量表达式,比如数组长度就必须是一个常量表达式
//  正确,长度5是由1个常量组成的常量表达式
int arr[5];  
// 正确,长度3+4是由2个常量组成的常量表达式
int arr2[3 + 4];  
int n = 10;
// 错误,长度n是由变量构成,不

是常量表达式
int arr3[n];
AI 代码解读

修饰变量

由此可以看出,只要是常量表达式,我们就可以通过constexpr来进行修饰,从而提高程序的效率,比如下面这样

// 正确,2+2是常量表达式,n将会在编译器进行计算
contexpr int n = 2 + 2;  
// 正确,n是一个常量表达式
int arr[n] = {
   11, 22, 33, 44};  
// 33
cout << arr[2] << endl;
AI 代码解读

修饰普通函数

constexpr还可以用于修饰函数的返回值,在C++11中被constexpr修饰的函数只能是非void类型的函数,而且必须非常简短,通常只有一句return表达式。不过在后续的C++14/17/20标准中进一步的放宽了这了限制,都可以通过编译了

constexpr int test() {
   
    return 1 + 1;
}
AI 代码解读

修饰构造函数

constexpr还可以用于修饰自定义类型,不过有一个前提条件,就是该自定义类型具有constexpr修饰的构造函数,并且该构造函数不能有具体实现,否则会编译报错

class Student {
   
   public:
    constexpr Student(const char* name, int age) : name_(name), age_(age) {
   }

    void print() const {
    cout << name_ << ' ' << age_ << '\n'; }

   private:
    const char* name_;
    int age_;
};

void test() {
   
    constexpr Student s{
   "zhangsan", 18};
    s.print();
}
AI 代码解读

总结

  • const可以修饰编译期和运行期的常量,而constexpr只能修饰编译期的常量
  • const在仍然可以通过const_cast类型转换来修改值,而constexpr是不可以修改的,其实可以将const理解为只读变量更符合其含义
  • const只能用于非静态成员函数,而constexpr可以和成员,非成员,构造函数一起使用
  • 再有常量表示的场景,尽可能的加上constexpr来让编译期进行计算
  • 但是大面积的constexpr也会面临相应的增加编译时间的风险

最后

为了方便其他设备和平台的小伙伴观看往期文章:

微信公众号搜索:Let us Coding,关注后即可获取最新文章推送

看完如果觉得有帮助,欢迎 点赞、收藏、关注

目录
打赏
0
0
0
0
244
分享
相关文章
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
【C++篇】深度解析类与对象(中)
在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。
【C++篇】深度解析类与对象(上)
在C++中,类和对象是面向对象编程的基础组成部分。通过类,程序员可以对现实世界的实体进行模拟和抽象。类的基本概念包括成员变量、成员函数、访问控制等。本篇博客将介绍C++类与对象的基础知识,为后续学习打下良好的基础。
基于红黑树的局域网上网行为控制C++ 算法解析
在当今网络环境中,局域网上网行为控制对企业和学校至关重要。本文探讨了一种基于红黑树数据结构的高效算法,用于管理用户的上网行为,如IP地址、上网时长、访问网站类别和流量使用情况。通过红黑树的自平衡特性,确保了高效的查找、插入和删除操作。文中提供了C++代码示例,展示了如何实现该算法,并强调其在网络管理中的应用价值。
C++ `noexcept` 关键字的深入解析
`noexcept` 关键字在 C++ 中用于指示函数不会抛出异常,有助于编译器优化和提高程序的可靠性。它可以减少代码大小、提高执行效率,并增强程序的稳定性和可预测性。`noexcept` 还可以影响函数重载和模板特化的决策。使用时需谨慎,确保函数确实不会抛出异常,否则可能导致程序崩溃。通过合理使用 `noexcept`,开发者可以编写出更高效、更可靠的 C++ 代码。
71 1
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
119 1
【C++11】包装器:深入解析与实现技巧
本文深入探讨了C++中包装器的定义、实现方式及其应用。包装器通过封装底层细节,提供更简洁、易用的接口,常用于资源管理、接口封装和类型安全。文章详细介绍了使用RAII、智能指针、模板等技术实现包装器的方法,并通过多个案例分析展示了其在实际开发中的应用。最后,讨论了性能优化策略,帮助开发者编写高效、可靠的C++代码。
73 2
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
280 4
告别头文件,编译效率提升 42%!C++ Modules 实战解析 | 干货推荐
本文中,阿里云智能集团开发工程师李泽政以 Alinux 为操作环境,讲解模块相比传统头文件有哪些优势,并通过若干个例子,学习如何组织一个 C++ 模块工程并使用模块封装第三方库或是改造现有的项目。

云原生

+关注

推荐镜像

更多
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等