热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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

这篇文章主要讲解了“C++中const与constexpr的区别是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“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 
    struct RemoveConst{
        typedef T Type;
    };
    
    template 
    struct RemoveConst{
        typedef T Type;
    };
    
    template 
    using RCType = typename RemoveConst::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移除,但这种操作可能引起Crash或者未知风险

    const char* pA = "sss";
    char* pB = const_cast(pA);
    auto pC = RCType(pA);
    std::cout << "type is the same: " << std::is_same::value << std::endl;
    std::cout << "pB Type Name: " << typeid(pB).name() << "pc Type Name: " << typeid(pC).name() << std::endl;
    //pB[0] = &#39;A&#39;;//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 <

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

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

    感谢各位的阅读,以上就是“C++中const与constexpr的区别是什么”的内容了,经过本文的学习后,相信大家对C++中const与constexpr的区别是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程笔记,小编将为大家推送更多相关知识点的文章,欢迎关注!


    推荐阅读
    • 类加载机制是Java虚拟机运行时的重要组成部分。本文深入解析了类加载过程的第二阶段,详细阐述了从类被加载到虚拟机内存开始,直至其从内存中卸载的整个生命周期。这一过程中,类经历了加载(Loading)、验证(Verification)等多个关键步骤。通过具体的实例和代码示例,本文探讨了每个阶段的具体操作和潜在问题,帮助读者全面理解类加载机制的内部运作。 ... [详细]
    • 单片微机原理P3:80C51外部拓展系统
        外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
    • Vue应用预渲染技术详解与实践 ... [详细]
    • 本文介绍了UUID(通用唯一标识符)的概念及其在JavaScript中生成Java兼容UUID的代码实现与优化技巧。UUID是一个128位的唯一标识符,广泛应用于分布式系统中以确保唯一性。文章详细探讨了如何利用JavaScript生成符合Java标准的UUID,并提供了多种优化方法,以提高生成效率和兼容性。 ... [详细]
    • HBase Java API 进阶:过滤器详解与应用实例
      本文详细探讨了HBase 1.2.6版本中Java API的高级应用,重点介绍了过滤器的使用方法和实际案例。首先,文章对几种常见的HBase过滤器进行了概述,包括列前缀过滤器(ColumnPrefixFilter)和时间戳过滤器(TimestampsFilter)。此外,还详细讲解了分页过滤器(PageFilter)的实现原理及其在大数据查询中的应用场景。通过具体的代码示例,读者可以更好地理解和掌握这些过滤器的使用技巧,从而提高数据处理的效率和灵活性。 ... [详细]
    • 在分析Android的Audio系统时,我们对mpAudioPolicy->get_input进行了详细探讨,发现其背后涉及的机制相当复杂。本文将详细介绍这一过程及其背后的实现细节。 ... [详细]
    • 深入解析 Lifecycle 的实现原理
      本文将详细介绍 Android Jetpack 中 Lifecycle 组件的实现原理,帮助开发者更好地理解和使用 Lifecycle,避免常见的内存泄漏问题。 ... [详细]
    • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
    • 在尝试对 QQmlPropertyMap 类进行测试驱动开发时,发现其派生类中无法正常调用槽函数或 Q_INVOKABLE 方法。这可能是由于 QQmlPropertyMap 的内部实现机制导致的,需要进一步研究以找到解决方案。 ... [详细]
    • Codeforces竞赛解析:Educational Round 84(Div. 2评级),题目A:奇数和问题
      Codeforces竞赛解析:Educational Round 84(Div. 2评级),题目A:奇数和问题 ... [详细]
    • 视频编码涉及多个关键参数,如比特率、帧率和采样率等。比特率(Bit Rate)是指单位时间内视频或音频文件的数据传输量,通常以千比特每秒(Kbps)为单位。这些参数对视频质量和文件大小有重要影响。帧率(Frame Rate)表示每秒钟显示的图像帧数,而采样率(Sample Rate)则指每秒从连续信号中提取并形成离散信号的次数。了解这些基础概念有助于更好地优化视频编码效果。 ... [详细]
    • 本文探讨了如何通过编程手段在Linux系统中禁用硬件预取功能。基于Intel® Core™微架构的应用性能优化需求,文章详细介绍了相关配置方法和代码实现,旨在帮助开发人员有效控制硬件预取行为,提升应用程序的运行效率。 ... [详细]
    • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
    • 在Android平台中,播放音频的采样率通常固定为44.1kHz,而录音的采样率则固定为8kHz。为了确保音频设备的正常工作,底层驱动必须预先设定这些固定的采样率。当上层应用提供的采样率与这些预设值不匹配时,需要通过重采样(resample)技术来调整采样率,以保证音频数据的正确处理和传输。本文将详细探讨FFMpeg在音频处理中的基础理论及重采样技术的应用。 ... [详细]
    • 在Linux系统中,网络配置是至关重要的任务之一。本文详细解析了Firewalld和Netfilter机制,并探讨了iptables的应用。通过使用`ip addr show`命令来查看网卡IP地址(需要安装`iproute`包),当网卡未分配IP地址或处于关闭状态时,可以通过`ip link set`命令进行配置和激活。此外,文章还介绍了如何利用Firewalld和iptables实现网络流量控制和安全策略管理,为系统管理员提供了实用的操作指南。 ... [详细]
    author-avatar
    mobiledu2502859163
    这个家伙很懒,什么也没留下!
    PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
    Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有