C++中const、constexpr, mutable的用法

本文章主要整理在c++中经常用到的const,constexpr, mutable三个关键字的用法,有些内容参考了下面这篇博客,在此提出感谢。
https://www.cnblogs.com/xkfz007/articles/2419540.html

1.const

对于使用const的原则,尽量在能使用const的地方使用const,既有利于code的阅读,也有利于让编译器帮忙检查一些错误。
a.const修饰普通变量和指针
修饰普通变量有两种写法:这两种写法的作用是一样的,都是表明该变量为一个常量,创建时必须初始化,不能修改。
另外,在默认情况下,const变量只在本文件有效,因为编译器在编译时会把const变量全部替换为其代表的值,比如下面的code,编译器在编译时会把所有的变量a替换为5,所以编译本文件时,编译器必须知道a的值,如果多个文件定义了同名的const变量,则代表不同的值。如果想多个文件共享一个const变量,则需要该变量在定义和声明的地方都需要用extern关键字。

const int a = 5;
int const a = 5;

但对于指针来说,则不同的写法有不同的作用
const char *a; //a所指向的变量为常量,也就是(*a)为常量
(char*) const a; //a为常量,不能改变,也就是a必须在创建的时候初始化,然后a不能再指向其他变量。
识别const到底是修饰指针还是指针所指的对象,还有一个较为简便的方法,也就是沿着*号划一条线: 如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量; 如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。
b.const修饰函数参数
const修饰函数参数比较常见,主要修饰指针形参和引用形参,一般建议如果该函数不修改传入的指针或引用所指向的变量,则最好声明为const,这样外部调用时传入的参数既可以是const,也可以是非const,也能告诉调用者此函数不会修改传入的变量。如果形参为变量,则是值传递,有没有const修饰都没用。最常用的用法就是如下两个函数:
void function(const char* val)
void function(const char& val)
另外像下面这样的用法也是没用的,比如
void function(char* const val)//因为对val来说也是值传递,所以用const修饰没有用处。

c.const修饰类对象,对象指针,对象引用
const也可以修饰某个类的对象,如AAA为自定义的一个类,则
const AAA aaa;表明aaa为常量对象,其中的任何成员都不能修改,该对象的非const成员函数也不能调用,另外const也可以修饰类内的成员变量和成员函数,如下就是修饰类内的成员函数。用const修饰的成员函数不能改变对象的成员变量,也不能调用类中任何非const成员函数。

class A
{
	void function() const;//const成员函数
}

constexpr变量
首先说明,常量表达式就是在编译阶段编译器可以确定其值的变量就称为常量表达式。示例如下:

const int buf_size = 512;//在编译时就可以确定其值,所以为常量表达式,可以用constexpr修饰。
const int sz = get_size(); //在编译时不能确定其值,所以sz不是常量表达式,不能用constexpr修饰,除非get_size()时一个constexpr修饰的函数

c++11的新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式,声明为constexpr的变量一定为一个常量,并且必须用常量表达式来初始化。下面两个变量都是常量表达式。

constexpr int mf = 20;
constexpr int limit = mf + 1;

用constexpr声明的指针为常量指针,也就是该指针为一个常量,必须指向nullptr或者一个constexpr常量,且一旦初始化,不能再指向其他变量,因为该指针为一个常量指针。
一般来说,如果你认定某个常量为常量表达式,就声明为constexpr。
常量表达式因为在编译阶段就已知,所以可以用作数组的长度,std::array的长度,比如:

constexpr int mf = 20;
char a[mf];

总的来说,所有的constexpr对象都是const对象,但不是所有的const都是constexpr对象。
constexpr函数
constexpr函数也是c++11新加的特性,是指能用于常量表达式的函数,constexpr函数的返回类型及所有形参的类型都得是字面值类型。编译时,编译器会将constexpr函数隐式的指定为内联函数。所以,constexpr函数一般是放在头文件中定义的,允许constexpr函数多次定义。

constexpr int new_sz() { return 42;}
constexpr int foo = new_sz();
constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }
constexpr size_t foo1 = scale(2);//编译正确,编译器可以算出来foo1的值,并且用算出来的值来替换foo1
int cnt = 2;
constexpr size_t foo2 = scale(cnt);//编译错误,编译器不能计算出foo2的值,因为cnt不是字面值。
size_t foo3 = scale(cnt);//编译正确,但foo3不是constexpr变量
constexpr int pow(int base, int exp) noexcept   //c++14 可以有多条语句 
{
	auto result = 1;
	for (int i = 0; i < exp; ++ i) result *= base;
	return result;
}

2.mutable

mutable的中文意思为“可变的,易变的”,与const正好为反义词,在c++中,mutable也是为了突破const的限制而设置的,被mutable修饰的变量(mutable只能修饰类的非静态数据成员)将永远处于可变的状态,即使在一个const函数中。
我们知道,假如类的成员函数不会改变对象的状态,那么这个成员函数一般会声明为const。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。下面是一个小例子:

class ClxTest {  
public:  
 void Output() const;
};
 void ClxTest::Output() const 
 {  
 cout << "Output for test!" << endl;
 } 
  void OutputTest(const ClxTest& lx) 
 {
    lx.Output();
 }

类ClxTest的成员函数Output是用来输出的,不会修改类的状态,所以被声明为const。 函数OutputTest也是用来输出的,里面调用了对象lx的Output输出方法,为了防止在函数中调用成员函数修改任何成员变量,所以参数也被const修饰。 假如现在,我们要增添一个功能:计算每个对象的输出次数。假如用来计数的变量是普通的变量的话,那么在const成员函数Output里面是不能修改该变量的值的;而该变量跟对象的状态无关,所以应该为了修改该变量而去掉Output的const属性。这个时候,就该我们的mutable出场了,只要用mutable来修饰这个变量,所有问题就迎刃而解了。下面是修改过的代码:

class ClxTest {
  public:
    ClxTest();
    ~ClxTest();
     void Output() const;
      int GetOutputTimes() const; 
  private:
      mutable int m_iTimes;
 }; 
 ClxTest::ClxTest() 
 {
   m_iTimes = 0; 
 } 
 ClxTest::~ClxTest() {}
 void ClxTest::Output() const
  {
    cout << "Output for test!" << endl;
     m_iTimes++; 
   } 
 int ClxTest::GetOutputTimes() const
  {
    return m_iTimes;
  } 
 void OutputTest(const ClxTest& lx)
  {
    cout << lx.GetOutputTimes() << endl;
     lx.Output();
     cout << lx.GetOutputTimes() << endl;
  }

计数器m_iTimes被mutable修饰,那么它就可以突破const的限制,在被const修饰的函数里面也能被修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值