const各种用法总结

const各种用法总结 

原文地址:http://www.cnblogs.com/jiabei521/p/3335676.html#2787569

1、const关键字
常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。
1.1 const使用方法
1.1.1 定义const对象
const修饰符可以把对象转变成常数对象,意思就是说利用const进行修饰的变量可以当做常数来使用
任何对该变量的修改都会导致编译错误。
const int bufferSize = 512;
bufferSize = 0;   //Error

const std::string hi = "hello";
const int i,j = 0;   // 错误,i未初始化
注意:因为常量不能被修改,所以定义时必须得被初始化。

类中的const成员变量必须通过初始化列表进行初始化。
class A
{
public:
A(int i);
void print();

const int &r;
private:
const int a;
static const int b;
};
const int A::b = 10;  //类中的静态变量不属于任何对象,它在定义时就有了内存区,应该被初始化,如果
不初始化不使用的话,也不会出错。
A::A(int i):a(i),r(a)
{
}

1.1.2 const对象默为局部变量
在全局作用域里定义非const变量时,它在整个程序中都可以访问,我们可以把一个非const变量定义在一
个文件中,就可以在另一个文件中使用这个变量。
//a.c
int counter; //definition
//b.c
extern int counter;  //use counter from a.h
++conunter;          //increments counter defined int a.h
在全局作用域声明的const变量只存在于那个文件中,不能被其他文件访问。通过指定const变量为extern,就
可以在整个程序中访问const对象。
//a.c
extern const int bufSize = fcn();
//b.c
extern const int bufSize;  //use bufSize from a.h
注意:非const变量默认为extern,要使const变量能够在其他文件中访问,必须在文件中显式指定它为extern。

1.1.3 const引用
const引用是指向const对象的引用:
const int iVal = 1024;
const int &refVal = iVal;  //两者均为const对象
int &ref2 = iVal;    //error,不能使用非const引用指向const对象,将"int &"类型的引用绑定到"const
int"类型的初始值设定项时,限定符被丢弃
可以读取但是不能修改refVal,因此,任何对refVal的赋值都是不合法的。这个限制有其意义,不能直接对iVal
赋值,因此不能通过使用refVal来修改iVal.
const引用可以初始化为不同类型的对象或者初始化为右值,比如字面常量:
int i = 42;
//仅对const引用合法
const int &r = 42;
const int &r2 = r + i;

同样的初始化对于非const引用是不合法的,而且会导致编译时错误。
将引用绑定到不同的类型
double dVal = 3.14;
const int &ri = dVal;
编译器会将这些代码转换为一下形式:
int temp = dVal;
const int &ri = temp; //编译器会创建一个int型的暂时变量存储dVal,然后将ri绑定到temp上
注意:引用在内部存放的是一个对象的地址,它是该对象的别名,对于不可寻址的值,如文字常量,以及不同类型的
对象,编译器为了实现引用,必须生成一个临时对象,引用实际上指向该对象,但用户不能访问它。
如果ri不是const,那么可以给ri赋一新值。这样做不会修改dVal的值,而是修改了temp,所以需要使用const引用
表示该变量时只读的。
注意:非const引用只能绑定到与该引用相同类型的对象。
const引用则可以绑定到不同相关的类型的对象或绑定到右值。

1.1.4 const对象的动态数组
如果我们在自由存储区中创建了数组存储内置类型的const对象,则必须为这个数组提供初始化,因为数组元素
都是const对象,无法赋值,实现这个要求的唯一方法是对数组做值初始化。
//Error
const int *pci_bad = new const int[100];
//Ok
const int *pci_ok = new const int[100]();
C++允许定义类型的const数组,但该类型必须提供默认构造函数:
const string *pcs = new string[100]; //这里便会调用string类型的默认构造函数初始化数组元素
1.1.5 指针和const限定符的关系
const限定符和指针结合起来常见的情况有:
(1)指向常量的指针【指向const对象的指针】
C++为了保证不允许使用指针改变所指的const值这个特性,强制要求这个指针也必须具备const特性:
const double *cptr;
这里cptr是一个指向double类型const对象的指针,const先顶了cptr指向的对象的类型,而并非cptr本身,所以cptr
本身并不是const。所以定义的时候并不需要对它进行初始化,如果需要的话,允许给cptr重新赋值,让其指向另一个
const对象。但不能通过cptr修改其所指对象的值。
*cptr = 42; //error
而我们将一个const对象的地址赋给一个普通的非const指针也会导致编译错误。
const double pi = 3.14;
double *ptr = π     //Error
const double *cptr = π  //Ok
不能使用void*指针保存const对象的地址,必须使用const void*类型的指针保存const对象的地址。
const int universe = 42;
const void *cpv = &universe;  //ok
void *pv = &universe; //Error universe是const的
允许把非const对象的地址赋给指向const对象的指针
double dval = 3.14;
cptr =  &dval;
不能通过cptr指针来修改dval的值,即使它指向的是非const对象。
不能使用指向const对象的指针修改基础对象,然而如果该指针指向非const对象,可用其他方式修改所指向的对象,
所以实际上,可以修改const指针所指向的值,但是不能通过const对象指针来进行而已。
dval = 3.1415926; //一个非const对象
*cptr = 3.1555;   //Error!!! cptr是指向const对象的,不能进行赋值
double *ptr = &dval;  //定义一个非const指针指向dval
*ptr = 2.155;      //将dval的值修改,此时*cptr的值就发生了改变

(2)常指针(const指针)
C++中还提供了const指针----本身的值不能被修改。
int errNumb = 0;
int *const curErr = &errNumb; //curErr是一个const指针
这是指向int型对象的const指针,与其他const量一样,const的值不能被修改,这意味着不能使curErr指向其他对象,
Const指针也必须在定义的时候初始化。
curErr = curErr; //错误,即使是赋值给相同的值

(3)指向常量的常指针(指向const对象的const指针)
const double pi = 3.1415926;
const double *const pi_ptr = π

1.2 函数和const限定符的关系
1.2.1 类中的const成员函数(常量成员函数)
在一个类中,任何一个不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数式,不慎修改了
数据成员,或者调用了非const成员函数,编译器将指出错误,提高了函数的健壮性。使用const修饰的函数叫做常成
员函数,只有常成员函数才有资格操作常量和常对象。
class Stack
{
private:
int m_num;
int m_data[100];
public:
void Push(int elem);
int Pop(void);

int GetCount(void) const;  //定义const成员函数
};


int Stack::GetCount(void) const
{
++m_num; //编译错误,企图修改成员变量m_num
Pop(); //编译错误,企图修改被const成员函数
return m_num;


1、函数重载
const是定义为const函数的组成部分,那么可以通过添加const实现函数重载
class R
{
public:
R(int r1,int r2)
{
R1=r1;
R2=r2;
}
void print();
void print() const;
private:
int R1,R2;
};
void R::print()
{
cout<<R1;
}
void R::print() const
{
cout<<R2;
}
void main()
{
R a(5,4);
a.print();
const R b(20,52);
b.print();
}
print成员函数实现了两个版本,输出结果为5,52。const对象默认调用const成员区。

1、const修饰函数返回值
const修饰函数返回值的含义和const修饰普通变量以及指针的含义基本相同。
//这个其实无意义,因为参数返回本身就是赋值
const int fun1();
//调用const int *pValue = fun2();
//我们可以把fun2()看作成一个变量,即指针内容不可变
const int * fun2();
//调用时int * const pValue = fun2();
//我们可以把fun2()看做成一个变量,即指针本身不可变
int* const fun3();  
一般情况下,函数的返回值为某个对象时,如果将其声明为const时,多用于操作符的重载。
通常不建议用const修饰函数的返回值类型为某个对象或某个对象的引用的情况,如果这样
则返回值具有const属性,则返回实例只能访问类A中的公有(保护)数据成员和const成员
函数,并且不允许对其进行复制操作。
1、const修饰函数参数
1)传递过来的参数在函数内不可以(无意义,因为var本身就是形参)
void function(const int var);
2)参数指针所指内容为常量不可变
void function(const char* var);
3)参数指针所指内容为常量不可变
void function(const char* var);
4)参数为引用,为了增加效率同时防止修改。修饰引用参数
void function(const Class& var);//引用参数在函数内不可以改变
void function(const TYPE& var);//引用参数在函数内为常量不可变
const引用传递和普通的函数传递的效果是一样的,他禁止对引用对象的一切修改,唯一不
同的是按值传递会先建立一个类对象的副本,然后传递过去,而他直接传递地址,所以这种
传递比按值传递更有效。
5)const限定符和static的区别
1]const定义的常量在超出其作用域之后其空间会被释放,而static定义的静态常量在函数执行
后不会释放其存储空间。
2] static表示的是静态的。类的静态成员函数、静态成员变量时和类相关的,而不是和类具体的
对象相关的。即使没有具体对象,也能调用类的静态成员函数和成员变量。一般类的静态函数几乎
是一个全局变量,只不过他的作用域限于包含它的文件中。
3] 在C++中,static静态变量不能在类的内部初始化。在类的内部只是声明,定义必须咋类定义体
的外部,通常在类的实现文件中初始化。如:double Account::Rate=2.25; static关键字只能用于
类定义体内部的声明中,定义时不能标示为static
4] 在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表,并且必须有构造
函数
5] const数据成员,只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建
多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,
因为类的对象没被创建时,编译器不知道const数据成员的值是什么。
6] const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的
常量,应该用类中的枚举常量来实现,或者static const。
class Test
{
public:
Test():a(0){}
enum {size1=100,size2=200};
private:
const int a;     //只能在构造函数的初始化列表初始化
static int b; //在类的实现文件中定义并初始化
const static int c;   //与static const int c,相同
};
int Test::b=0;    //static成员变量不能在构造函数初始化列表中初始化,因为它不属于某个对象
const int Test::c=0;   //注意:给静态成员变量赋值时,不需要加static修饰符。但要加const

6)const成员函数主要目的是防止成员函数修改对象的内容。即const成员函数不能修改成员变量的值,

但可以访问成员变量。当方法成员函数时,该函数只能是const成员函数。
7)static成员函数主要目的是作为类作用域的全局函数。不能访问类的静态数据成员。类的静态成员
函数没有this指针,这导致:1、不能直接存取类的非静态成员变量,调用非静态成员函数 2、不能声
明为virtual。

2、关于static、const、static const、const static成员的初始化问题
1)类里的const成员初始化
class foo
{
public:
foo():i(100){}
private:
const int i=100; //error,不能在类中初始化
};
//
foo::foo():i(100)
{

}
2)类里的static成员初始化
类中的static变量时属于类的,不属于某个对象,它在整个程序的运行过程中只有一个副本,因此在定义
对象时,对变量进行初始化,就是不能使用构造函数进行初始化,正确的初始化方法是:
数据类型 类名::静态数据成员名=值
class foo
{
public:
foo();
private:
static int i;
};

int foo::i=20;
/*
这表明:
1、初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆
2、初始化时不加该成员的访问权限控制符private,public等
3、初始化时使用作用域运算符来表明它所属的类,因此,静态数据成员是类的成员而不是对象的成员
*/

3)类里的static const和const static成员初始化(这两种写法是一致的)
class Test
{
public:
static const int mask1;
const static int mask2;
};
const Test::mask1=0xffff;
const Test::mask2=0xffff;
//他们的初始化没有区别,一个是静态常量,一个是常量静态
//静态都将存储在全局变量区域,其实最后结果都一样
//可能在不同的编译器内,不同处理,但最后结果都一样


完整的例子
#include <iostream>
using namespace std;
class A
{
public:
A(int a);
static void print();//静态成员函数
private:
static int aa;//静态数据成员的声明
static const int count;//常量静态数据成员(可以在构造函数中初始化)
const int bb;//常量数据成员
};
int A::aa=0;   //静态成员的定义+初始化
const int A::count=25;  //静态常量成员定义+初始化
A::A(int a)::bb(a)
{
aa+=1;
}
void A::print()
{
cout<<"count="<<count<<endl;
cout<<"aa="<<aa<<endl;
}
void main()
{
A a(10);
A::print();    //通过类访问静态成员函数
a.print();   //通过对象访问静态成员函数
}

3、const的难点
如果函数需要传入一个指针,面试官可能会问是否需要为该指针加上const,把
const加在指针不同的位置有什么区别;如果写的函数需要传入的参数是一个复杂
类型的实例,面试官可能会问传入值参数或者引用参数有什么区别,什么时候为
传入的引用参数加上const.

const是用来声明一个常量的,当你不想让一个值被改变时就用const,const int max
和int const max是没有区别的,都可以。不涉及到指针const容易理解。
int b=100;
const int *a=&b;  //[1]
int const *a=&b;  //[2]
int* const a=&b;  //[3]
const int* const a=&b;  //[4]

如果const位于星号的左侧,则const就是用来修改指针所指向的变量,即指针指向的对象
为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。

因此,[1]和[2]的情况相同,都是指针所指向的内容为常量(const放在变量声明符的位置无关),
这种情况下不允许对内容进行更改操作,如不能*a=3,[3]为指针本身是常量,而指针所指向的
内容不是常量,这种情况下不能对指针本身进行更改操作,如a++是错误的,[4]为指针本身和
指向的内容均为常量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值