const用法小结


1.     定义const常量:在定义一个const常量时必须赋以初值,而且之后不能更改。

定义格式:(1) const 类型名 变量名 =常量或者常量表达式; 

(2) 类型名 const 变量名 = 常量或者常量表达式;

如: const int iVal = 8;

         iVal= 10;//错误,不允许更改

 

2.     const引用:指向const对象的引用。

定义格式:(1)const <类型说明符> &<变量名> =

(2)<类型说明符> const &<变量名> =

普通引用不能绑定到const 对象,但const 引用可以绑定到非const 对象。

const int iVal = 8;

int &p = iVal;  //错误

const int &p = iVal;  //正确

非const 引用只能绑定到与该引用同类型的对象。

const 引用则可以绑定到不同但相关的类型的对象或绑定到右值。

(1) constint &r = 100; // 绑定到字面值常量

(2) inti = 50;

const int &r2 = r + i; // 引用r绑定到右值

(3) doubledVal = 3.1415;

const int &ri = dVal;   // 整型引用绑定到double 类型

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

int temp= dVal;   // 创建临时变量

const int&ri = temp;  // 把引用指向临时变量

 

3.     const修饰指针

(1)    指向const对象的指针:指针本身可以重新指向,但是所指向的内容不能修改。

定义格式:const <类型说明符> *<变量名>

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

   允许把非const 对象的地址赋给指向const 对象的指针:

   int iVal;

   const int*p = &iVal;

   iVal = 10;

   *p = 11; //错误 此时*p 是只读的,不能修改其值。

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

例如:

const doubledVal = 3.14;

double*ptr = &dVal; // 错误

double*ptr = const_cast<double*>(&dVal);

// const_cast是C++中标准的强制转换,C语言使用:double *ptr = (double*)&dVal;

 

       (2).const 指针:指针指向不能改变

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

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

例如:

int iVal1=0;

int iVal2= 10;

int *constp = &iVal;

p = &iVa2l;//错误 指针的指向不能被修改。

*p = 1;// 正确  指针所指向的基础对象可以修改。

(3) 指向const 对象的const 指针:指针本身和指向的内容均不能改变

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

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

例如:

const doublepi = 3.14159;

const doubledVal = 3.14;

const double*const pi_ptr = &pi;

pi_ptr= &dVal; // 错误指针的指向不能被修改。

*pi_ptr= dVal; // 错误指针所指向的基础对象也不能被修改。

 

4.     const 修饰一般函数

(1)  修饰函数参数

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(); // 注意千万不要返回指向局部对象的指针,因为一旦函数结束,局部对象被释放,返回的指针变成了指向一个不再存在的对象的悬垂指针。

 

5.     const 在类中的使用

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_tsize; // 对于const的成员变量,[1]必须在构造函数里面进行初始化;[2]只能通过初始化成员列表来初始化;[3]试图在构造函数体内对const成员变量进行初始化会引起编译错误。

例如:

A::A(size_tsz):size(sz) // ok:使用初始化成员列表来初始化

{

}

A::A(size_tsz)

(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 Ab(100);

b.func();// 调用函数[2]

如何在const成员函数中对成员变量进行修改???

       mutable

class A

{

public:

    A::A(int i):m_data(i){}

    void SetValue(int i) const { m_data = i; }

private:

    mutable int m_data; // 这里处理

};

(3)修饰类对象

const Aa; // 类对象a 只能调用const 成员函数,否则编译器报错。

(4)修饰类成员函数的返回值

const Aoperator+(const A &) const; // 前一个const 用来修饰重载函数operator+的返回值,可防止返回值作为左值进行赋值操作。

例如:

A a;

A b;

A c;

a + b =c; // 出错: 如果在没有const 修饰返回值的情况下,编译器不会报错。

 

C和C++中const区别

1.只读变量 & 常量

在C语言中,被const修饰的就是常量吗?真的能不动如山吗?编译如下代码,你觉得如何?

   const int a = 10;

   int *pa = &a;

   *pa = 20;

   printf("%d/n",*pa);

printf("%d/n",a);

编译器并不会像你想的那样会报错,只是给出警告(warning C4090: “初始化”: 不同的“const”限定符)你运行时才发现,a的值已经被改掉了。变成了 20

关键字const并不能把变量变成常量!在一个符号前面加上限定符const只是表示不能被赋值,换句话说,它的值对这个符号来说是只读的,但是并不能防止通过程序的内部(或者说外部)的方法来修改这个值。

所在C语言中被const修饰的变量只是一个只读的变量而已。如:

const intn = 100;

int a[n];(C不提倡这样写,可以用宏代替)

但是在C++中不是这样的,在C++中被const的修饰毫无疑问是常量,它的值在编译时就被确定了。

   const int a = 10;

   int *pa = &a;

   *pa = 20;

   printf("%d/n",*pa);

上面代码在C++中是错误的,肯定会出现编译错误。( error C2440: “初始化”: 无法从“const int *”转换为“int *” )

而下面代码在C++中是很普遍的。

const intn = 100;

int a[n];

而且这样的表达是很普遍的很值得提倡的表示法(C++中不提倡使用宏,宏可以被常量、内联函数替代)。

在C++中的const属性是可以被去掉的,通过const_cast就可以强转掉。你试过这样的代码吗?

#include<iostream>

using namespacestd;

int main()

{

    const int a = 10;

    const int *pa = &a;

    int* pb = const_cast<int*>(pa);

    *pb = 20;

    cout << *pa << endl;

    cout << *pb << endl;

    cout << a << endl;

         return 0;

}

输出结果为

20

20

10

2. 内连接和外连接的区别

const variable 在定义时必须初始化,是文件内部可见。这是因为c++中,const变量默认是内连接的(internal linkage)。(原因见后)
也就是说它只能在定义它的文件内部使用,连接时其它编译单元看不见它。例如:

const int i = 0 ;    // 而且编译器一般不为const variable分配内存,而是将它放符号表(symbol table),以便编译时实现常量折叠(constant folding)。

但是,若是进行如下定义:extern const int i;

则将强制编译器为变量分配内存空间。因为,extern 意味着使用外部链接(external linkage)。

当你对一个const variable 取地址时,也会进行内存分配。例如:

const int i = 9;

long address =  (long ) &i;

由上面我们可以看出,编译器并不总是能够成功的避免为常量分配内存,上述两种情况就必须分配内存。所以const variable如果默认是外连接,就有可能导致同一个常量在不同的cpp文件中都分配了内存(比如多个文件中都要求取得一个常量的地址)。这就会让编译器认为同一常量出现重复定义而报错

而如果常量默认为内连接,这就意味着该常量只在当前定义它的文件内部有效,而连接器不会试图去连接其他编译单元里的常量。这样,即使多个cpp文件中有相同名字的一个常量也不会发生冲突。如此,编译器就可有效的实现常量折叠(constant folding)

另外,有上面我们知道,在C++中,const int i = 0;  i的值是在符号表里,这就意味着i的值是编译期间可见的。所以,如下代码是可行的:

    const  int i = 0;

    const int j = i + 1;  // ok! 因为i的值在编译时知道,j也是const

这里和C中的const有所不同,C中的const默认为外连接,所以总是会为其分配内存空间。这就意味着,在C中

   const  int bufsize = 100;

在编译期间,编译器并不知道 bufsize的值。所以,下面的代码有误:

      const  int bufsize  = 100;

      char buf[bufsize]; // error

还有一点不同的是:

      const int bufsize;

这句代码在C中是可以的,编译器认为这是一个声明,某处有为这个常量分 配内存,但是在C++中,这句代码是不可以的。

可以有两种方式修改:

(1) const int bufsize = 100;  //定义时初始化

(2) extern const int;  // 当然,这句在C中也是可以的

////////////////////////////////////////////////////////////////////////////////////

在C语言中:
const int size;
这个语句是正确的,因为它被C编译器看作一个声明,指明在别的地方分配存储空间.但在C++中这样写是不正确的.C++中const默认是内部连接,如果想在C++中达到以上的效果,必须要用extern关键字.

C++中,const默认使用内部连接.而C中使用外部连接.
内连接:编译器只对正被编译的文件创建存储空间,别的文件可以使用相同的表示符或全局变量.C/C++中内连接使用static关键字指定.
外连接:所有被编译过的文件创建一片单独存储空间.一旦空间被创建,连接器必须解决对这片存储空间的引用.全局变量和函数使用外部连接.通过extern关键
字声明,可以从其他文件访问相应的变量和函数.
************************C++代码******************************
header.h
const int test = 1;
test1.cpp
#include
#include "header.h"
using namespace std;
int main()
{
cout << "in test1 :" << test << endl;
}
test2.cpp
#include
#include "header.h"
using namespace std;
void print()
{
cout << "in test2:" << test << endl;
}
以上代码编译连接完全不会出问题,但如果把header.h改为:
extern const int test = 1;
在连接的时候,便会出现以下错误信息:
test2 error LNK2005: "int const test" (?test@@3HB) 已经在 test1.obj 中定义
因为extern关键字告诉C++编译器test会在其他地方引用,所以,C++编译器就会为test创建存储空间,不再是简单的存储在名字表里面.所以,当两个文件同时包含header.h的时候,会发生名字上的冲突.
此种情况和C中const含义相似:
header.h
const int test = 1;
test1.c
#include
#include "header.h"
int main()
{
printf("in test1:%d\n",test);
}
test2.c
#include
#include "header.h"
void print()
{
printf("in test2:%d\n",test);
}
错误消息:
test3 fatal error LNK1169: 找到一个或多个多重定义的符号
test3 error LNK2005: _test 已经在 test1.obj 中定义
C++中,是否为const分配空间要看具体情况.
如果加上关键字extern或者取const变量地址,则编译器就要为const分配存储空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值