C++const详解,const各种使用方式,一次完整总结

2022-12-21 0 477

一 有关通常自表达式

新闻稿或表述的文件格式如下表所示:

const <类别tcsh> <cp> = <自表达式或自表达式表达式>; [1] <类别tcsh> const <cp> = <自表达式或自表达式表达式>; [2] [1]和[2]的表述是全然同构的。

比如:

医美int(或其它内建类别:float,double,char)

const int bufSize = 512;

或是

int const bufSize = 512;

即使自表达式在表述后就无法被修正,因此表述时要调用。

bufSize = 128; // error:attempt to write to const object const string cntStr = “hello!”; // ok:initialized const i, j = 0; // error: i is uninitialized const

非const表达式预设为extern。

const 第一类预设为文档的局部表达式。要使const表达式能在其它的文档中出访,要隐式地选定它为extern。

比如:

const int bufSize = 512; // 返回值只局限于表述此表达式的文档 extern const int bufSize = 512; // extern用作不断扩大返回值,返回值为整座源文件(只有extern 坐落于表达式内部时,才能所含调用式)

二 有关字符串及内部结构体

新闻稿或表述的文件格式如下表所示:

const <类别tcsh> <字符串名>[<大小不一>]…… [1] <类别tcsh> const <字符串名>[<大小不一>]…… [2] [1]和[2]的表述是全然同构的。

比如:

医美int(或其它内建类别:float,double,char)

const int cntIntArr[] = {1,2,3,4,5};

或是

int const cntIntArr[] = {1,2,3,4,5}; struct SI { int i1; int i2; }; const SI s[] = {{1,2},{3,4}}; // 上面的两个const都是表达式集合,编译器会为其分配内存,因此无法在编译期间采用其中的值(比如:int temp[cntIntArr[2]],这样的话编译器会报告无法找到自表达式表达式)

三 有关引用

新闻稿或表述的文件格式如下表所示:

const <类别tcsh> &<cp> = …… [1] <类别tcsh> const &<cp> = …… [2] [1]和[2]的表述是全然同构的。

比如:

const int i = 128; const int &r = i;(或是 int const &r = i;)

const 引用就是指向const 第一类的引用。

普通引用无法绑定到const 第一类,但const 引用能绑定到非const 第一类。

const int ii = 456; int &rii = ii; // error int jj = 123; const int &rjj = jj; // ok

非const 引用只能绑定到与该引用同类别的第一类。

const 引用则能绑定到不同但相关的类别的第一类或绑定到右值。

比如:

1.const int &r = 100; // 绑定到字面值自表达式 2.int i = 50; const int &r2 = r + i; // 引用r绑定到右值 3.double dVal = 3.1415; const int &ri = dVal; // 整型引用绑定到double 类别

编译器会把以上代码转换成如下表所示形式的编码:

int temp = dVal; // create temporary int from double const int &ri = temp; // bind ri to that temporary

四 有关指针

1.指向const 第一类的指针(指针所指向的内容为自表达式)

新闻稿或表述的文件格式如下表所示(表述时能不调用):

const <类别tcsh> *<cp> …… [1] <类别tcsh> const *<cp> …… [2] [1]和[2]的表述是全然同构的。

比如:

const int i = 100; const int *cptr = &i;

或是

int const *cptr = &i; [cptr 是指向int 类别的const 第一类的指针] 允许把非const 第一类的地址赋给指向const 第一类的指针,比如: double dVal = 3.14; // dVal is a double; its value can be change const double *cdptr = &dVal; // ok;but cant change dVal through cdptr

无法采用指向const 第一类的指针修正基础第一类。然而如果该指针指向的是一个没const 第一类(如cdptr),可用其它方法修正其所指向的第一类。

如何将一个const 第一类合法地赋给一个普通指针???

比如:

const double dVal = 3.14; double *ptr = &dVal; // error double *ptr = const_cast<double*>(&dVal); // ok: const_cast是C++中标准的强制转换,C语言采用:double *ptr = (double*)&dVal;

2.const 指针(指针本身为自表达式)

新闻稿或表述的文件格式如下表所示(表述时要调用):

<类别tcsh> *const <cp> = ……

比如:

int errNumb = 0; int iVal = 10; int *const curErr = &errNumb; [curErr 是指向int 型第一类的const 指针] 指针的指向无法被修正。 curErr = &iVal; // error: curErr is const 指针所指向的基础第一类能修正。 *curErr = 1; // ok:reset value of the object(errNumb) which curErr is bind

3.指向const 第一类的const 指针(指针本身和指向的内容均为自表达式)

新闻稿或表述的文件格式如下表所示(表述时要调用):

const <类别tcsh> *const <cp> = ……

比如:

const double pi = 3.14159; const double dVal = 3.14; const double *const pi_ptr = π [pi_ptr 是指向double 类别的const 第一类的const 指针] 指针的指向无法被修正。 pi_ptr = &dVal; // error: pi_ptr is const 指针所指向的基础第一类也无法被修正。 *pi_ptr = dVal; // error: pi is const

五 有关通常表达式

1.修饰表达式的参数

class A; void func1(const int i); // i无法被修正 void func3 (const A &rA); // rA所引用的第一类无法被修正 void func2 (const char *pstr); // pstr所指向的内容无法被修正

2.修饰表达式的返回值

返回值:const int func1(); // 此处返回int 类别的const值,意思指返回的原表达式里的表达式的初值无法被修正,但是表达式按值返回的这个表达式被制成副本,能无法被修正就没有了意义,它能被赋给任何的const或非const类别表达式,全然不需要加上这个const关键字。 [*注意*]但这只对于内部类别而言(即使内部类别返回的肯定是一个值,而不会返回一个表达式,不会作为左值采用,否则编译器会报错),对于用户自表述类别,返回值是自表达式是非常重要的(后面在类里面会谈到)。 返回引用:const int &func2(); // 注意千万不要返回局部第一类的引用,否则会报运行时错误:即使一旦表达式结束,局部第一类被释放,表达式返回值指向了一个对程序来说不再有效的内存空间。 返回指针:const int *func3(); // 注意千万不要返回指向局部第一类的指针,即使一旦表达式结束,局部第一类被释放,返回的指针变成了指向一个不再存在的第一类的悬垂指针。

六 有关类

class A { public: void func(); void func() const; const A operator+(const A &) const; private: int num1; mutable int num2; const size_t size; };

1.修饰成员表达式

const size_t size; // 对于const的成员表达式, [1]要在构造表达式里面进行调用; [2]只能通过调用成员列表来调用; [3]试图在构造表达式体内对const成员表达式进行调用会引起编译错误。

比如:

A::A(size_t sz):size(sz) // ok:采用调用成员列表来调用 { } A::A(size_t sz)

2.修饰类成员表达式

void func() const; // const成员表达式中不允许对数据成员进行修正,如果修正,编译器将报错。如果某成员表达式不需要对数据成员进行修正,最好将其新闻稿为const 成员表达式,这将大大提高程序的健壮性。

const 为表达式重载提供了一个参考

class A { public: void func(); // [1]:一个表达式 void func() const; // [2]:上一个表达式[1]的重载 …… }; A a(10); a.func(); // 调用表达式[1] const A b(100); b.func(); // 调用表达式[2]

如何在const成员表达式中对成员表达式进行修正???

下面提供几种形式(只提倡采用第一种,其它形式不建议采用)

(1)标准形式:mutable

class A { public: A::A(int i):m_data(i){} void SetValue(int i){ m_data = i; } private: mutable int m_data; // 这里处理 };

(2)强制转换:static_cast

class A { public: A::A(int i):m_data(i){} void SetValue(int i) { static_cast<int>(m_data) = i; } // 这里处理 private: int m_data; };

(3)强制转换:const_cast

class A { public: A::A(int i):m_data(i){} void SetValue(int i) { const_cast<A*>(this)->m_data = i; } // 这里处理 private: int m_data; };

(4)采用指针:int *

class A { public: A::A(int i):m_data(i){} void SetValue(int i) { *m_data = i; } // 这里处理 private: int *m_data; };

(5)未表述的处理形式

class A { public: A::A(int i):m_data(i){} void SetValue(int i) { int *p = (int*)&m_data; *p = i } // 这里处理 private: int m_data; }; 注意:这里虽然说能修正,但结果是未表述的,避免采用!

3.修饰类第一类

const A a; // 类第一类a 只能调用const 成员表达式,否则编译器报错。

4.修饰类成员表达式的返回值

const A operator+(const A &) const; // 前一个const 用来修饰重载表达式operator+的返回值,可防止返回值作为左值进行赋值操作。 比如: A a; A b; A c; a + b = c; // errro: 如果在没有const 修饰返回值的情况下,编译器不会报错。

七 采用const的一些建议

1.要大胆的采用const,这将给你带来无尽的益处,但前提是你要搞清楚原委;

2.要避免最通常的赋值操作错误,如将const表达式赋值,具体可见思考题;

3.在参数中采用const应该采用引用或指针,而不是通常的第一类实例,原因同上;

4.const在成员表达式中的三种用法(参数、返回值、表达式)要很好的采用;

5.不要轻易的将表达式的返回值类别定为const;

6.除了重载操作符外通常不要将返回值类别定为对某个第一类的const引用;

八 cons有什么主要的作用?

1.能表述const自表达式,具有不可变性。

比如:

const int Max=100; int Array[Max];

2.便于进行类别检查,使编译器对处理内容有更多了解,消除了一些隐患。

比如:

void f(const int i) { ………} 编译器就会知道i是一个自表达式,不允许修正;

3.能避免意义模糊的数字出现,同样能很方便地进行参数的调整和修正。

同宏表述一样,能做到不变则已,一变都变!如(1)中,如果想修正Max的内容,只需要:

const int Max=you want;

即可!

4.能保护被修饰的东西,防止意外的修正,增强程序的健壮性。

还是上面的例子,如果在表达式体内修正了i,编译器就会报错;

比如:

void f(const int i) { i=10;//error! }

5.为表达式重载提供了一个参考。

class A { …… void f(int i) {……} file://一个表达式 void f(int i) const {……} file://上一个表达式的重载 …… };

6.能节省空间,避免不必要的内存分配。

比如: #define PI 3.14159 file://自表达式宏 const doulbe Pi=3.14159; file://此时并未将Pi放入ROM中 …… double i=Pi; file://此时为Pi分配内存,以后不再分配! double I=PI; file://编译期间进行宏替换,分配内存 double j=Pi; file://没有内存分配 double J=PI; file://再进行宏替换,又一场分配内存!

const表述自表达式从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,因此,const表述的自表达式在程序运行过程中多于一份拷贝,而#define表述的自表达式在内存中有若干个拷贝。

7.提高了效率。

编译器通常不为普通const自表达式分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的自表达式,没有了存储与读内存的操作,使得它的效率也很高。

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务