C++中const关键字 理解

const:符号常量  使用符号常量写出的代码更容易维护;指针是边读边移动,而不是边写边移动,许多函数参数是只读不写的。

  const最常见用途是作为数组的界和switch分情况标号(也可以用枚举符代替),分类如下:

 

  常变量:  const 类型说明符 变量名

  常引用:  const 类型说明符 &引用名

  常对象:  类名 const 对象名

  常成员函数:  类名::fun(形参) const

  常数组:  类型说明符 const 数组名[大小]    

  常指针:  const 类型说明符* 指针名 ,类型说明符* const 指针名

在常变量(const 类型说明符 变量名)、常引用(const 类型说明符 &引用名)、常对象(类名 const 对象名)、 常数组(类型说明符 const 数组名[大小]), const” 与 “类型说明符”或“类名”(其实类名是一种自定义的类型说明符) 的位置可以互换。如:

     const int a=5;   与   int const a=5; 等同

     类名 const 对象名  与   const 类名 对象名 等同

 

用法1:常量
    取代了C中的宏定义,声明时必须进行初始化(!c++类中则不然)。const限制了常量的使用方式,并没有描述常量应该如何分配。如果编译器知道了某const的所有使用,它甚至可以不为该const分配空间。最简单的常见情况就是常量的值在编译时已知,而且不需要分配存储。―《C++ Program Language》
    用const声明的变量虽然增加了分配空间,但是可以保证类型安全。
    C标准中,const定义的常量是全局的,C++中视声明位置而定。

用法2:指针和常量
    使用指针时涉及到两个对象:该指针本身和被它所指的对象。将一个指针的声明用const“预先固定”将使那个对象而不是使这个指针成为常量。要将指针本身而不是被指对象声明为常量,必须使用声明运算符*const。
    所以出现在 * 之前的const是作为基础类型的一部分:
char *const cp; //到char的const指针
char const *pc1; //到const char的指针
const char *pc2; //到const char的指针(后两个声明是等同的)
    从右向左读的记忆方式:
cp is a const pointer to char. 故cp不能指向别的字符串,但可以修改其指向的字符串的内容

pc2 is a pointer to const char. 故*pc2的内容不可以改变,但pc2可以指向别的字符串

且注意:允许把非 const 对象的地址赋给指向 const 对象的指针,不允许把一个 const 对象的地址赋给一个普通的、非 const 对象的指针。(C++语言强制要求指向const对象的指针也必须具有const特性)

用法3:const修饰函数传入参数
    将函数传入参数声明为const,以指明使用这种参数仅仅是为了效率的原因,而不是想让调用函数能够修改对象的值。同理,将指针参数声明为const,函数将不修改由这个参数所指的对象。
    通常修饰指针参数和引用参数:
void Fun( const A *in); //修饰指针型传入参数
void Fun(const A &in); //修饰引用型传入参数

用法4:修饰函数返回值
    可以阻止用户修改返回值。返回值也要相应的付给一个常量或常指针。

若函数的返回值是指针,且用const修饰,则函数返回值指向的内容是常数,不可被修改,此返回值仅能赋值给const修饰的相同类型的指针。如:

1  const int * f1(){
2      int * p;
3      p = new int;
4      *p = 1;
5     return p;
6  }
7  int main(){
8     const int * p1;
9     p1 = f1();
10    return 0;
11 }

 

若第8行改为int *p1;则编译时报错:“[8] error: invalid conversion from 'const int*' to 'int*'” (编译器code::block);

若主函数改为:

7  int main(){
8     const int * p1;
9     p1 = f1();
10    *p1 = 2;
11    return 0;
12 }

 

则编译时报错:"[10] error: assignment of read-only location '* p1'"  (编译器code::block);

如果函数返回值是数值(by value),因C++中,返回值会被复制到外部临时的存储单元中,故const 修饰,中没有任何价值的。例:不要把函数int fun1() 写成const int func1()。
同理不要把函数A GetA(void) 写成const A GetA(void),其中A 为用户自定义的数据类型。

如果返回值是对象,将函数A fun2() 改写为const A & fun2()的确能提高效率。但此要注意,要确定函数究竟是想返回一个对象的“copy”,还是仅返回对象的“别名”即可,否则程序会出错。

 

用法5:const修饰成员函数(c++特性)
const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数;
const对象的成员是不能修改的,而通过指针维护的对象确实可以修改的;
const成员函数不可以修改对象的数据,不管对象是否具有const性质。编译时以是否修改成员数据为依据进行检查。

 

  面向对象程序设计中,为了体现封装性,通常不允许直接修改类对象的数据成员。若要修改类对象,应调用公有成员函数来完成。为了保证const对象的常量性,编译器须区分不安全与安全的成员函数(即区分试图修改类对象与不修改类对象的函数)。例如,

1 const Screen blankScreen;
2 blankScreen.display(); // 对象的读操作
3 blankScreen.set(‘*’); // 错误:const类对象不允许修改

        在C++中,只有被声明为const的成员函数才能被一个const类对象调用。

        要声明一个const类型的类成员函数,只需要在成员函数参数列表后加上关键字const,例如,

1 class Screen {
2 public:
3 char get() const;
4 };

 

        在类体之外定义const成员函数时,还必须加上const关键字,例如

1 char Screen::get() const {
2 return _screen[_cursor];
3 }

 

        若将成员成员函数声明为const,则该函数不允许修改类的数据成员。例如,

1 class Screen {
2 public:
3 int ok() const {return _cursor; }
4 int error(intival) const { _cursor = ival; }
5 };

 

        在上面成员函数的定义中,ok()的定义是合法的,error()的定义则非法。

        值得注意的是,把一个成员函数声明为const可以保证这个成员函数不修改数据成员,但是,如果据成员是指针,则const成员函数并不能保证不修改指针指向的对象,编译器不会把这种修改检测为错误。例如,

 1 class Name {
 2   public:
 3     void setName(const string &s) const;
 4   private:
 5     char *m_sName;
 6 };
 7 
 8 void setName(const string &s) const {
 9   m_sName = s.c_str(); // 错误!不能修改m_sName;
11   for (int i = 0; i < s.size(); ++i) 
12     m_sName[i] = s[i]; // 不好的风格,但不是错误的
13 }

 

        虽然m_Name不能被修改,但m_sName是char *类型,const成员函数可以修改其所指向的字符。

        const成员函数可以被具有相同参数列表的非const成员函数重载,例如,

1 class Screen {
2 public:
3 char get(int x,int y);
4 char get(int x,int y) const;
5 };

 

        在这种情况下,类对象的常量性决定调用哪个函数。

const Screen cs;
Screen cc2;
char ch = cs.get(0, 0); // 调用const成员函数
ch = cs2.get(0, 0); // 调用非const成员函数

 

小结:

1)const成员函数可以访问非const对象的非const数据成员、const数据成员,也可以访问const对象内的所有数据成员;

2)非const成员函数可以访问非const对象的非const数据成员、const数据成员,但不可以访问const对象的任意数据成员;

3)作为一种良好的编程风格,在声明一个成员函数时,若该成员函数并不对数据成员进行修改操作,应尽可能将该成员函数声明为const 成员函数。

 

用const 修饰函数的参数 

如果参数作输出用,不论它是什么数据类型,也不论它采用“指针传递”还是“引用传递”,都不能加const 修饰,否则该参数将失去输出功能。const 只能修饰输入参数:
如果输入参数采用“指针传递”,那么加const 修饰可以防止意外地改动该指针,起到保护作用
例如StringCopy 函数:
 
void StringCopy(char *strDestination, const char *strSource);
 
其中strSource 是输入参数,strDestination 是输出参数。给strSource 加上const修饰后,如果函数体内的语句试图改动strSource 的内容,编译器将指出错误。
如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const 修饰。
例如不要将函数void Func1(int x) 写成void Func1(const int x)。同理不要将函数void Func2(A a) 写成void Func2(const A a)。其中A 为用户自定义的数据类型。
对于非内部数据类型的参数而言,象void Func(A a) 这样声明的函数注定效率比较底。因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。
为了提高效率,可以将函数声明改为void Func(A &a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。但是函数void Func(A &a) 存在一个缺点:
“引用传递”有可能改变参数a,这是我们不期望的。解决这个问题很容易,加const修饰即可,因此函数最终成为void Func(const A &a)。
以此类推,是否应将void Func(int x) 改写为void Func(const int &x),以便提高效率?完全没有必要,因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。
问题是如此的缠绵,我只好将“const &”修饰输入参数的用法总结一下。
对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。
 对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &x)。

 

2 用const 修饰函数的返回值

如果给以“指针传递”方式的函数返回值加const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
例如函数:
const char * GetString(void);
如下语句将出现编译错误:
char *str = GetString();
正确的用法是
const char *str = GetString();
如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。
例如不要把函数int GetInt(void) 写成const int GetInt(void)。
同理不要把函数A GetA(void) 写成const A GetA(void),其中A 为用户自定义的数据类型。
如果返回值不是内部数据类型,将函数A GetA(void) 改写为const A & GetA(void)的确能提高效率。但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错。
函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。

 

原文链接: https://www.cnblogs.com/lvmf/p/10793170.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    C++中const关键字 理解

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/294349

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月15日 下午3:47
下一篇 2023年2月15日 下午3:48

相关推荐