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

小万哥
• 阅读 357

很多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;    
}

修饰指针

虽然指针也是一种变量,不过当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;    

修饰函数

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;
};

const引用

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

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

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

void test(const Student &s) {
    ...
}

constexpr关键字

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

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

修饰变量

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

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

修饰普通函数

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

constexpr int test() {
    return 1 + 1;
}

修饰构造函数

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();
}

总结

  • const可以修饰编译期和运行期的常量,而constexpr只能修饰编译期的常量
  • const在仍然可以通过const_cast类型转换来修改值,而constexpr是不可以修改的,其实可以将const理解为只读变量更符合其含义
  • const只能用于非静态成员函数,而constexpr可以和成员,非成员,构造函数一起使用
  • 再有常量表示的场景,尽可能的加上constexpr来让编译期进行计算
  • 但是大面积的constexpr也会面临相应的增加编译时间的风险
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
PPDB:今晚老齐直播
【今晚老齐直播】今晚(本周三晚)20:0021:00小白开始“用”飞桨(https://www.oschina.net/action/visit/ad?id1185)由PPDE(飞桨(https://www.oschina.net/action/visit/ad?id1185)开发者专家计划)成员老齐,为深度学习小白指点迷津。
Wesley13 Wesley13
3年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
MBR笔记
<bochs:100000000000e\WGUI\Simclientsize(0,0)!stretchedsize(640,480)!<bochs:2b0x7c00<bochs:3c00000003740i\BIOS\$Revision:1.166$$Date:2006/08/1117
Stella981 Stella981
3年前
Jenkins 插件开发之旅:两天内从 idea 到发布(上篇)
本文首发于:Jenkins中文社区(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fjenkinszh.cn)!huashan(https://oscimg.oschina.net/oscnet/f499d5b4f76f20cf0bce2a00af236d10265.jpg)
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这