zoukankan      html  css  js  c++  java
  • C++ const各种用法总结

    转载自: https://www.toutiao.com/i6644404128439075332/,有删改。附参考:https://www.jb51.net/article/118141.htm

    一 一般常量

    声明或定义的格式如下:

    const <类型说明符> <变量名> = <常量或常量表达式>; [1]
    <类型说明符> const <变量名> = <常量或常量表达式>; [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 <类型说明符> <数组名>[<大小>]…… [1]
    <类型说明符> 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 <类型说明符> &<变量名> = …… [1]
    <类型说明符> const &<变量名> = …… [2]
    [1][2]的定义是完全等价的。
    

    例如:

    const int i = 128;
    const int &r = i;(或者 int const &r = i;

    const 引用就是指向const 对象的引用。

    普通引用不能绑定到const 对象,但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 <类型说明符> *<变量名> …… [1]
    <类型说明符> const *<变量名> …… [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 can't 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 指针(指针本身为常量)

    声明或定义的格式如下(定义时必须初始化):

    <类型说明符> *const <变量名> = ……
    

    例如:

    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 <类型说明符> *const <变量名> = ……

    例如:

    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.不要轻易的将函数的返回值类型定为const6.除了重载操作符外一般不要将返回值类型定为对某个对象的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常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

  • 相关阅读:
    什么是根文件系统
    构建基本的嵌入式Linux根文件系统
    “文件系统”与“根文件系统”详解
    C#中NameValueCollection类用法详解
    别把西红柿连续种在同一块地里
    asp.net 服务器控件的 ID,ClientID,UniqueID 的区别
    不要为框架作过多的假设
    构件技术
    asp.net中控件id,clientid,uniqueid的区别
    系统架构图怎么画
  • 原文地址:https://www.cnblogs.com/wanghongze95/p/13842463.html
Copyright © 2011-2022 走看看