我的编程空间,编程开发者的网络收藏夹
学习永远不晚

详解C++中的const和constexpr

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

详解C++中的const和constexpr

一.const常量与#define比较

  • define只是简单的替换,没有类型,const可以做到防窜改与类型安全。
  • 而且#define会在内存中可能(有几次替换就有几次拷贝)有多份拷贝,对于字面值常量加不加const都一样,例如:const char* arr = “123”;,储存在常量区,只有一份拷贝;对于局部对象,常量存放在栈区,例如:void add(){const char crr[] = “123”;},这里“123”本应储存在栈上,但编译器可能会做某些优化,将其放入常量区;对于全局对象,常量存放在全局/静态存储区;用const会比#define使用更少的空间,效率更高。
  • 这里有一个小例子:char* brr = "123"; char drr[] = "123";前者字符串123存在常量区,不能通过brr去修改"123"的值;后者"123"保存在栈区,可以通过drr去修改。
  • 现在C++除了一些特定用法,推荐用const,inline,enum等替换宏——来自《Effective C++》条款02

二.const修饰

1.修饰普通变量,必须初始化

const int a = 10; 表示int对象a,是一个常量,不可以改变值,从编译器生成二进制角度看,生成的a存放在.rodata段,也就是只读(readonly)区域。不过并不绝对,有的时间统计优化等级开的高,也不取地址,可能会优化成立即数在.text段中。

2.修饰类变量和成员变量


class cAAA{
public:
    cAAA(int a) : m_iV(a){}
    const int GetValue() const {return m_iV;}
    void AddValueOneTime(){m_iChangeV++;}
private:
    const int m_iV;
public:
    mutable int m_iChangeV;
    static const int m_iStaticV;
};
static const int m_iStaticV = 1000;

const cAAA aa(100);
aa.GetValue();
aa.m_iChangeV++;
  • cAAA类成员m_iV是const变量,必须放到初始化列表中进行初始化,不能进行赋值。
  • 对于静态常成员,与普通静态成员类似,推荐放到类外.cpp中初始化。
  • aa只能调用const函数,如aa.GetValue(),不能调用非常成员函数aa.AddValueOneTime()。
  • 对于这种const对象,又想修改成员,可以在成员声明加上mutable,这样const对象aa也可以修改m_iChangeV,这种用法比较少。

3.修饰成员函数

  • 表示这个函数可以被const对象调用,也可以被普通对象调用,不会改变对象的成员,也就是说更像一种只读不写型的逻辑运算,所以有些人推荐类成员函数,可以都加上const。有一个小技巧,当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复;但反过来不行,const函数内部也必须只能调用const函数—— 《Effective C++》条款03
  • 有一点要注意,编译器强制实施bitwase constness,但编写程序时应该使用conceptual constness,解决编译器的bitwase constness属性就用到了上述的mutable。关于介绍bitwase constness的具体表现可以参考《Effective C++》条款03。

4.修饰指针


const char* p1;
char const *p2;
char* const p3;
const char* const p4;

对于初学者来说这大概是很难理解的一个知识点,怎么区分这四个呢?记住秘诀,直接从右向左读就一招制敌了。

  • p1是一个指针,指向char字符常量,表示p1所指对象内容不可以改,所指地址可以改。
  • p2同p1,写法不同,两者等价。
  • p3是一个常量,且是个指针,指向char字符,表示p3所指对象内容可以改,所指地址不可以改。
  • p4是一个常量,且是个指针,指向char字符常量,表示p4所指对象内容不可以改,且所指地址也不可以改。
  • 相对来说p1,p2是最常用传参或者返回值的手段。

5.修饰引用

  • 修饰引用和对象差不多,对象内容不可以改变。作为函数参数传参数,不存在copy开销,这是比较推荐的写法,例如:拷贝构造函数,赋值构造,STL里用于比较的函数或者仿函数,详情请参阅《Effective C++》条款20。bool Less(const cAAA& left, const cAAA& right);

float dValue = 1.05f;
const int& a = dValue;

const int iTemp = dValue;
const int& a = iTemp;
  • 因为常引用不能改变,这种情况下编译器会创建一个临时变量来处理隐式转换,我们实际是对临时变量进行了常引用。

三.const转换

  • 一般来说,从T*转换到const T*是比较简单的,且编译器支持的隐式转换,也可以显示的用模板处理,例如我们简单写一下RemoveConst模板,最后用using化名一下。但从const T*到T*就麻烦一些,推荐使用const_cast。

template <typename T>
struct RemoveConst{
    typedef T Type;
};

template <typename T>
struct RemoveConst<const T>{
    typedef T Type;
};

template <typename T>
using RCType = typename RemoveConst<T>::Type;

四.顶层const与底层const

  • 简单来说const修饰的对象本身不能改变就是顶层const,但如果是指针或者引用的对象不能改变,则称为底层const。
  • const int cV = 10; cV是顶层const,本身不能改变
  • char const *p2; p2是底层const,p2本身值可以改变,但所指内容不可以改变
  • char* const p3; p3是顶层const,p3的本身值不可以改变
  • const char* const p4; p4既是顶层const,又是底层const
  • 注:对于上述模板RCType是无法移除p2这种底层const,如果要移除,请用const_cast<T*>移除,但这种操作可能引起Crash或者未知风险

const char* pA = "sss";
char* pB = const_cast<char*>(pA);
auto pC = RCType<decltype(pA)>(pA);
std::cout << "type is the same: " << std::is_same<decltype(pB), decltype(pC)>::value << std::endl;
std::cout << "pB Type Name: " << typeid(pB).name() << "pc Type Name: " << typeid(pC).name() << std::endl;
//pB[0] = 'A';//error, Segmentation fault

五.C++11新引入的constexpr

  • 这个关键字表示这是一个常量表达式,是一个编译期就可以确认的值,最常用于模板中,例如模板递归求值。
  • 它可不只是变量,例如:

const int iSize1 = sizeof(int);
const int iSize2 = GetSize();

iSize1是个常量,编译期的,但iSize2就不一定,它虽然不能改变,但要到GetSize()执行结束,才能知道具体值,这与常量一般在编译期就知道的思想不符,解决这个问题的方法就是改为:constexpr int iSize2 = GetSize(); 这样要求GetSize()一定要能在编译期就算出值,下面几个例子中GetSizeError()就会编译失败。GetFibo函数,编译期就已经递归计算出值。


constexpr int GetSize(){
  return sizeof(int) + sizeof(double);
}

constexpr int GetSizeError(){
  return random();
}

constexpr int GetCalc(int N){
  return N <= 1 ? 1 : N * GetCalc(N - 1);
}

const int iSize1 = sizeof(int);
constexpr int iSize2 = GetSize();
//constexpr int iSize3() = GetSizeError();
constexpr int iSize4 = GetCalc(10);
std::cout << iSize1 << " " << iSize2 << " " << iSize4 <<std::endl;

当然我们可以用模板写GetCalc函数:


template <int N>
struct TCalc{
  static constexpr int iValue = N * TCalc<N-1>::iValue;
};

template <>
struct TCalc<1>{
  static constexpr int iValue = 1;
};
std::cout << TCalc<10>::iValue << std::endl;

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

详解C++中的const和constexpr

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

怎么解析C++中的const和constexpr

本篇文章为大家展示了怎么解析C++中的const和constexpr,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一.const常量与#define比较define只是简单的替换,没有类型,cons
2023-06-21

C++11的const, const expression和constexpr有什么作用

这篇文章主要讲解了“C++11的const, const expression和constexpr有什么作用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++11的const, const
2023-06-19

C++中const与constexpr的区别是什么

这篇文章主要讲解了“C++中const与constexpr的区别是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++中const与constexpr的区别是什么”吧!一.const常量
2023-06-21

C#中的const和readonly关键字详解

本文详细讲解了C#中的const和readonly关键字,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2022-11-13

const在c和c++中的区别

在 c 和 c++ 中,const 关键字用于声明常量。在 c 中,const 只能修饰变量,不能修饰指针或数组;在 c++ 中,const 可以修饰变量、指针和数组。主要的差异在于:作用域、指针修改和初始化要求不同。const 在 C 和
const在c和c++中的区别
2024-04-28

C++const的各种用法详解

const名叫常量限定符,用来限定特定变量,以通知编译器该变量是不可修改的。习惯性的使用const,可以避免在函数中对某些不应修改的变量造成可能的改动。本文主要谈谈const的用法,感兴趣的同学可以参考阅读
2023-05-17

c++中const的作用和用法

const 在 c++ 中表示不可变性,用于强制变量、函数参数或类成员保持常量,使其值在程序运行期间不可修改。具体用法包括:修饰变量以强制不可变性,如 const int my_age = 30;;修饰函数参数以传递不可变值,如 void
c++中const的作用和用法
2024-05-01

C++和C中const的区别是什么

本篇内容介绍了“C++和C中const的区别是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!const,这个词字面意思为:常数。这就表示
2023-06-21

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录