Skip to content

[c++11]cv限定符

关键字constvolatile统称为cv限定符(cv qualifiers

const

参考:const (C++)

语法如下:

const declaration ;
member-function const ;

声明值

用于数据声明时,const关键字指定对象或变量不可修改。c++使用const声明代替#define预处理器指令进行常量定义

int main() {
   const int i = 5;
   i = 10;   // error: assignment of read-only variable ‘i’
   i++;   // error: assignment of read-only variable ‘i’
}

数组大小

c++编程中,可使用const声明变量作为数组大小

const int maxarray = 255;
char store_char[maxarray];  // allowed in C++; not allowed in C

const指针

参考:C++中指针常量和常量指针的区别

const指针就是常量指针,即指针指向的是常量,这个常量指的是指针的值(地址),而不是地址指向的值

  • 指向const常量的指针可以重新赋值,即指针能够指向另一个地址
  • 指向const常量的指针只能赋值给同样声明为const常量的指针,两者指向同一个地址
  • 使用常量指针作为函数参数能够避免函数体中参数被修改
void f(const char *te) {
    te = "asdfadsf";

    cout << te << endl;
}

int main() {
    const char *te = "asdfa";
    const char *ttee = te;
    te = "13414";

    cout << ttee << endl;
    f(te);
    cout << te << endl;
}

结果:

asdfa         // 两个指针指向同一个地址
asdfadsf      // 指针地址可修改
13414         // 函数参数不可修改

const对象

如果对象声明为const,则只能调用const成员函数

class Cls {
public:
    Cls(int a, char b) : a(a), b(b) {}

    void setA(int a);

    void setA(int a) const;

private:
    int a;
    char b;
};

void Cls::setA(int a) {
    this->a = a;
    cout << this->a << endl;
}

void Cls::setA(int a) const {
    cout << "const " << a << endl;
}

int main() {
    const Cls cls(1, 'c');
    cls.setA(33);

    Cls cls2(2, 'd');
    cls2.setA(11);
}

声明成员函数

声明成员函数为const,表示该函数为只读函数(即常量成员函数),不会修改调用对象

常量成员函数(constant member function)无法修改任何非静态数据成员和调用任何非常量成员函数

示例如下:

class Cls {
public:
    void setA(int a) const;
}

void Cls::setA(int a) const {
    cout << "const " << a << endl;
}

c vs. c++

c语言中,const拥有外部链接,所以文件声明如下:

const int i=2; // 源文件初始化
extern const int i; // 其他模块使用

c++语言中,const拥有内部链接,所以必须显式添加extern关键字:

extern const int i=2; // 源文件初始化
extern const int i; // 其他模块使用

如果想要在c文件中调用c++ const变量,需要初始化如下:

extern “C” const int x=10;

所以c语言中,常量通常定义在源文件;c++语言中,常量也可定义在头文件中

constexpr

参考:constexpr (C++)

constexprc++11提出的关键字,意为常量表达式(constant expression

constexpr除了const的功能外,还可用于函数和构造器声明,表示其值或返回值是常量

constexpr整数值可用于替代需要const整数的任何位置,例如模板参数和数组声明中。当一个值可以在编译时而不是在运行时计算时,它可以帮助您的程序更快地运行,并且使用更少的内存

constexpr变量

const变量和constexpr变量的主要区别在于前者的初始化可在运行时推导,而后者必须在编译时完成初始化

  • 如果变量具有literal类型并已初始化,则可以使用constexpr声明该变量。如果初始化是由构造函数执行的,则必须将该构造函数声明为constexpr
  • 如果引用的对象已由常量表达式初始化,并且在初始化期间调用的任何隐式转换也是常量表达式,则可以将引用声明为constexpr
  • constexpr变量或函数的所有声明都必须具有constexpr说明符

constexpr函数

constexpr函数可以在编译时计算其返回值。例如初始化constexpr变量或提供非类型模板参数。当其参数为constexpr值时,constexpr函数生成编译时常量。当使用非constexpr参数调用时,或者在编译时不需要它的值时,它在运行时像常规函数一样生成一个值(这种双重行为使您不必编写同一函数的constexprnon constexpr版本

constexpr函数或构造函数是隐式内联的

以下规则适用于constexpr函数:

  • constexpr函数必须只接受和返回literal类型
  • constexpr函数可以是递归的
  • 它不能是virtual的。如果封闭类具有任何virtual基类,则不能将构造函数定义为constexpr
  • 函数体可以定义为=default=delete
  • 函数体不能包含goto语句或try
  • constexpr模板的显式专用化可以声明为constexpr
  • constexpr模板的显式专用化不必也是constexpr

const vs. constexpr

参考:C++ const 和 constexpr 的区别?

const未区分编译期常量和运行期常量,constexpr表示编译期可计算

C 里面,const 很明确只有「只读」一个语义,不会混淆。C++ 在此基础上增加了「常量」语义,也由 const 关键字来承担,引出来一些奇怪的问题。C++11 把「常量」语义拆出来,交给新引入的 constexpr 关键字

C++11 以后,建议凡是「常量」语义的场景都使用 constexpr,只对「只读」语义使用 const

示例如下:

// constexpr 声明factorial可以参与编译期的运算
constexpr int factorial(int n) {
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

int main() {
    std::cout << "4! = " << factorial(4) << endl; // computed at compile time

    volatile int k = 4; // disallow optimization using volatile
    std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time
}

volatile

参考:

C 和 C++ 的 volatile 关键字为什么给编程者造成了如此大的误解?

深入理解C++中的volatile关键字

  • 阻止编译器调整操作volatile变量的指令顺序,提供对特殊地址的稳定访问
  • 阻止编译器为了提高速度将一个变量缓存到寄存器内而不写回,生成对应代码直接存取原始内存地址