一文让你彻底明白C++中的const

在抽象的最高层次上,const做两件事:

* 一种保护你自己的方式(类似于private)

* 对编译器的一种指示,表明标记为const的对象适合于程序的数据段。换句话说,属于只读数据(ROM-able)。

可以通过例子来看下const的应用。第一个例子中,使用const覆盖了整个例子:

void fun(int i, std::string const & str)
{
 i = 0;     //ok.
 str = "";    //error!
 int const n = 42;
 n = 2;     //error!
}

第二种情况只适用于静态初始化的名称空间-作用域变量(又称全局变量):

int const pi = 3; //ROM-able
std::vector<int> const ivec = {/* ... */}; //Not ROM-able, might allocate.

对声明为const的变量的任何写操作都被显示为未定义行为。这支持const全局变量在ROM中的位置。

如果一个变量定义在ROM中,对它的写操作很可能会使程序崩溃,这取决于平台。如果一个变量不在ROM中,对它的写操作只会改变它的值。这两种情况的结合就是为什么对const变量执行写操作的行为是未定义行为而不是错误。

如果真的需要改写一个const变量的值,可以通过`const_cast`来改写:

void fun(int i, std::string const & str)
{
 i = 0; //ok.
 const_cast<std::string &>(str) = ""; //Also ok (maybe).
}

然而,const_cast并不能避免你在尝试写入声明为const的变量时永远不会遭遇未定义行为的陷阱。

std::string str = "";
fun(0, str); // Ok.
std::string const const_str = "";
fun(0, const_str); // Undefined Behavior!!

因此,只有在确实需要时才使用const_cast,并且只有在知道要写入的底层变量是如何声明的情况下才使用。

**那么,究竟在什么时候什么地方使用const?**

答案就是**Everywhere**。将每个变量声明为const,除非您知道它将被写入。更一般地,在编译器接受的任何地方添加const。

int foo(int arg)
{
 int const x = compute_intermediate_result(arg);
 int const y = compute_other_intermediate_result(x);
 return something_computed_from(x, y);
}

优化器视角下的const

为了优化目的,编译器通常不能使用一致性进行优化。

int get_value(some_class const & x, int const at)
{
 int offset = compute_offset(at);
 return x[offset];
}

此时,在这些函数参数中使用const对优化器没有帮助。x上的const不起作用,因为x已经通过引用传递了。没有x的副本,编译器不知道x是否声明为const。在参数at上的const不能帮助我们,因为at是一个拷贝,它可以以任何方式装入寄存器。
如果编译器可以看到const对象的声明,它有时可以使用其一致性进行优化。

std::vector<int> const vec = { 1, 2, 3 };

int main()
{
 // This may generate code that indexes into vec, or it may generate
 // code that loads an immediate 2.
 return vec[1];
}

如果您想保证这样的优化,您可以在c++11或以后的版本中使用constexpr。使用constexpr声明的变量只对可以静态初始化的类型进行编译,因此,如果编译了它,就会得到一个ROM-able的对象。

constexpr std::array<int, 3> arr = { 1, 2, 3 };

int main()
{
 // This generates code that loads an immediate 2 on every
 // compiler I tried.
 return arr[1];
}

constexpr只处理字面常量类型(literal types)。这些类型与你可能在C中找到的类型相似。任何被声明为const的int或其他整数值都可以像文字一样使用。

void foo(int const arg)
{
 int const size = 2; 

 int array_0[2];  // Ok.
 int array_1[arg]; // Error! arg is a runtime value.
 int array_2[size]; // Ok.
}

静态const类成员

如果声明一个类成员static const,就很像声明一个全局const变量。

int const global_size = 3;

struct my_struct
{
 static int const size = 2;
};

std::array<int, global_size> make_global_array()
{ return {}; }

std::array<int, my_struct::size> make_my_struct_array()
{ return {}; }

但因为这是c++,所以有个问题。如果定义的static const整数值超越界限,则它可能无法被当作字面常量使用,例如一下的例子。

struct my_struct
{
 static int const size;
};

std::array<int, my_struct::size> make_my_struct_array() // Error!
{ return {}; }

int const my_struct::size = 2;

这是的错误时因为编译器在解析foo()时不知道要为my_class::size使用什么值。如果你希望像使用全局const整数值一样使用static const整数值,请始终将它们声明为内联(inline)。

总结

到此这篇关于C++中const的文章就介绍到这了,更多相关C++中的const内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

1. Use const to protect yourself from silly mistakes, and use it everywhere.

2. You can use const on globals that you want in ROM, but prefer constexpr.

3. Use const_cast sparingly, or not at all.

4. When you do use const_cast, be careful of the UB that might result, including crashes.

5. Use const globals and stack variables instead of macros for named values that are "as good as literals".

6. Define static const integral data members inline, if possible.

(0)

相关推荐

  • c++ const引用与非const引用介绍

    const引用是指向const对象的引用. 复制代码 代码如下: const int i = 10; const int &ref = i; 可以读取ref,但不能修改.这样做是有意义的,因为i本身就不可修改,当然也不能通过ref来修改了.所以也就有将const变量赋值给非const引用是非法的. 复制代码 代码如下: int &ref1 = i; // error: nonconst reference to a const object 非const引用是指向非const类型变量的引用

  • C++中const的实现机制深入分析

    问题 C语言以及C++语言中的const究竟表示什么?其具体的实现机制又是如何实现的呢? 本文将对这两个问题进行一些分析,简单解释const的含义以及实现机制. 问题分析 简单的说const在C语言中表示只读的变量,而在C++语言中表示常量.关于const在C与C++语言中的使用以及更多的区别,以后有时间另开一贴说明. 那么const究竟是如何实现的呢? 对于声明为const的内置类型,例如int,short,long等等,编译器会如何实现const的本意?那么对于非内置类型是否也是与内置数据类

  • c++ 尽量不要使用#define 而是用const、enum、inline替换。

    例如:这里程序文件开头有如下#define语句 复制代码 代码如下: #define N 10 #define PI 3.14 #define MAX 10000 #define Heigth 6.65 ... ... 假设这里程序运行出错误,而且就是在我们使用这些常量有错误,此时编辑器应该会抛出错误信息.如果该信息提示6.65这里有错误,Ok如果你运气好你正好记得或者程序简单一眼能找到6.65表示什么,如果程序很复杂,而且报出6.65的文件是引用该文件,不记得,那么你会困惑这是什么?或许会花大

  • C/C++中CONST用法总结(推荐)

    1.修饰常量时: const int temp1; //temp1为常量,不可变 int const temp2; //temp2为常量,不可变 2.修饰指针时: 主要看const在*的前后,在前则指针指向的内容为常量,在后则指针本身为常量: const int *ptr; //*ptr为常量: int const *ptr; //*ptr为常量: int* const ptr; //ptr为常量: const int * const ptr; //*ptr.ptr均为常量: 3.const修饰

  • C/C++中static,const,inline三种关键字详细总结

    一.关于staticstatic 是C++中很常用的修饰符,它被用来控制变量的存储方式和可见性,下面我将从 static 修饰符的产生原因.作用谈起,全面分析static 修饰符的实质. static 的两大作用: 一.控制存储方式 static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间. 引出原因:函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,大家知道,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保

  • c++ 类中const成员变量的赋值方法

    在头文件的类的定义中定义了一个const成员变量c++ 规则: 1.类定义中不能进行初始化,因为头文件中类的定义只是一个声明,并没有分配真正空间,因此变量是不存在的,因此是不能赋值的. 2.const 定义的变量是不能赋值 这可如何是好,声明中不能赋值,声明完还不能赋值.又不能不赋值. 解决方案: 1.在构造函数后的参数初始化列表中初始化 2.将const变量同时声明为 static 类型进行初始化. Eg: #include <iostream> class CTestA { public:

  • C++中的类型转换static_cast、dynamic_cast、const_cast和reinterpret_cast总结

    前言 这篇文章总结的是C++中的类型转换,这些小的知识点,有的时候,自己不是很注意,但是在实际开发中确实经常使用的.俗话说的好,不懂自己写的代码的程序员,不是好的程序员:如果一个程序员对于自己写的代码都不懂,只是知道一昧的的去使用,终有一天,你会迷失你自己的. C++中的类型转换分为两种: 1.隐式类型转换: 2.显式类型转换. 而对于隐式变换,就是标准的转换,在很多时候,不经意间就发生了,比如int类型和float类型相加时,int类型就会被隐式的转换位float类型,然后再进行相加运算.而关

  • C++ const修饰变量和修饰函数介绍

    const修饰变量 关于const最常见的一个面试题是这样的:char *const和const char*有什么区别,大家都知道const修饰符代表的是常量,即const修饰的变量一旦被初始化是不能被更改的,这两个类型一个代表的是指针不可变,一个代表指针指向内容不可变,但具体哪个对应哪个,很多人一直搞不清楚. 有这样一个规律,const修饰的是它前面所有的数据类型,如果const在最前面,那么把它和它后面第一个数据类行交换.比如上面的const char*交换之后就是char const *,

  • c++中const的使用详解

    Const 是C++中常用的类型修饰符,常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的. 1.定义常量(1)const修饰变量,以下两种定义形式在本质上是一样的.它的含义是:const修饰的类型为TYPE的变量value是不可变的. TYPE const ValueName = value;      const TYPE ValueName = value; (2)将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初始化,仅仅作为声

  • C++中的const和constexpr详解

    C++中的const可用于修饰变量.函数,且在不同的地方有着不同的含义,现总结如下. const的语义 C++中的const的目的是通过编译器来保证对象的常量性,强制编译器将所有可能违背const对象的常量性的操作都视为error. 对象的常量性可以分为两种:物理常量性(即每个bit都不可改变)和逻辑常量性(即对象的表现保持不变).C++中采用的是物理常量性,例如下面的例子: struct A { int *ptr; }; int k = 5, r = 6; const A a = {&k};

随机推荐