C++ const详解(附带static)

说明:const/static区别与联系在文章末尾,并且,const修饰参数,不能实现函数的重载。

const的基本概念

const叫常量限定符,用来限定特定变量,以告知编译器该变量是不可修改的。习惯性的使用const,可以避免在函数中对某些不应修改的变量造成可能的改动。

const修饰基本数据类型

1.const修饰一般常量及数组

const int a = 10;                        int const a = 10;
const int arr[3] = {1,2,3}             int const arr[3] = {1,2,3};

2.const修饰指针变量*及引用变量&
指针(pointer):是用来指向实际内存的变量,一般来说,指针是整型,而且一般的为十六进制的输出格式。
引用(reference):是其相应的变量的别名,用于向函数提供直接访问参数(而不是参数的副本)的途径,与指针相比,引用是一种受限制的指针类型,或者说是指针的一个自己,而从其功能上来看,似乎可以说引用是指针功能的一种高级实现。

const修饰指针(*)

const int *a ;//非常量数据的常量指针,指针常量,这个指针是一个常量,指这个值不能被这个指针常量修改,但是这个指针可以修改。
int const *a;//指针常量
int *const a;//常量的指针,常量指针,指这个指针不可以被修改,但是这个指针指向的内容可以被修改。
const int * const a;//常量数据的常量指针。

const修饰引用(&)
int const &a = x;
const int &a = x;
这个不可以a++;
int &const a = x; //这种方式定义是C、C++编译器未定义,虽然不会报错,但是该句效果和int &a一样。
此时是可以a++的。

定义const对象

const修饰符可以把对象转变成常数对象,意思就是说利用const进行修饰的变量的值在程序的任意位置将不能再被修改,就如同常数一样使用!任何修改该变量的阐释都会导致编译错误。
因为常量在定义之后就不能被修改,所以定义时必须初始化:
对于类中的const成员变量必须通过初始化列表进行初始化。

const对象默认为文件的局部变量
全局作用域里定义非const变量时,他在整个程序中都可以访问,我们可以把一个非const变量定义在一个文件中,假设已经做了合适的声明,就可以在另外的文件中使用这个变量

与其他变量不同,除非特别说明,在全局作用域声明的const变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问通过制定const变量为extern,就可以在整个程序中访问const对象。
注意:非const变量默认为extern,要使const变量能够在其他文件中访问,必须在文件中显式地制定他为extern。

const引用
const引用是指向const对象的引用:

const int ival = 1024;
const int &refVal = ival;//两者均为const对象
int $ref2 = ival;//错误!不能使用非const引用指向const对象

可以读取但不能修改refVal,因此任何对refVal的复制都是不合法的。这个限制有其意义:不能直接对ival赋值,因此不能通过使用refVal来修改ival。同理,用ival初始化ref2也是不合法的:ref2是普通的非const引用,因此可以用来修改ref2指向的对象的值。通过ref2指向的对象的值。通过ref2对ival赋值会导致修改const对象的值,为防止这样的修改,需要规定将普通的引用绑定到const对象这样是不合法的。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上。
注意:引用在内部存放的是一个对象的指针,他是该对象的别名。对于不可寻址的值,如文字常量,以及不同类型的对象,编译器为了实现引用,必须生成一个临时对象,引用实际上指向该对象,但用户不能访问它。

如果直接double dval =3.14;int &ri = dval;是不行的。(非常量限定错误)

注意:非const引用只能绑定到与该引用相同类型的对象。const引用则可以绑定到不同但相关的类型的对象或绑定到右值。

const对象的动态数组

如果我们在自由存储区中创建的数组存储了内置类型的const对象,则必须为这个数组提供初始化:因为数组元素都是const对象,无法赋值。实现这个要求的唯一方法是对数组做值初始化。

//error
const int *p = new const int [100];
//ok 
const int *p2 = new const int[100]();

C++允许定义类类型的const数组,但该类类型必须提供默认的构造函数:

const string *p = new string[100];
//这里便会调用string类的默认构造函数初始化数组元素。

指针和const限定符的关系(重点!)
const限定符和指针结合起来常见的情况有以下几种:
1.指向常量的指针(指向const对象的指针)
C++为了保证不允许使用指针改变所指的const值这个特性,强制要求这个指针也必须具备const特性:

const double *p;

这里p是一个指向double类型const对象的指针,const先顶了p指向的对象的类型,而并非p本身,所以cptr本身并不是const。所以定义的时候并不需要对他进行初始化,如果需要的话,允许给p重新赋值,让其所指向另一个const对象。但不能通过p修改其所指对象的值。

不能使用void指针保存const对象的地址,必须使用const void类型的指针保存const对象的地址

const int p = 42;
const void *p2 = &p; //OK
void p3  = &p ;//error

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

double dval = 3.14;
int const *cptr = &dval;

一样我们不能通过cptr指针来修改dval的值,可以修改const指针所指向的值,只不过是不能通过cptr对象指针来进行而已!

常量指针就不废话了

就是这个指针是一个常量,指针不能改指向,只能改内容。

函数和const限定符的关系(重点!)

1.类中的const成员函数(常量成员函数)
在一个类中,任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其他非const成员函数,编译器将之处错误,这无疑会提高程序的健壮性。使用cosnt关键字进行说明的成员函数,称为常成员函数。只有常成员函数才有资格操作常量或常对象,没有使用const关键字说明的成员函数不能用来操作常对象。常成员函数说明格式如下:
<类型说明符><函数名>(<参数表>)const;

其中,const是加载函数说明后面的类型修饰符,他是函数类型的一个组成部分,因此在函数实现部分也要带const关键字。
荔汁:

class Stack
{
public:
	int num;
	int data[100];
public:
	void  Push(int elem);
	int pop(void);
	int GetCount(void)	const;
};
int Stack::GetCount(void) const
{
	++num;//编译错误,企图修改数据成员num;
	Pop();//编译错误,企图非const成员函数
	return num;
}

函数重载
既然const是定义为const函数的组成部分,那么就可以通过添加const实现函数重载

class R
{
public:
	R(int r1,int r2)
	{
		R1 = r1;
		R2 = r2;
	}
	void print();
	void print() const ;
public:
	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();
}

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 (const A test = A 实例) 或某个对象的引用为const( const A &test = A实例),则返回值具有const属性,则返回实例只能访问类A中的共有(保护)数据成员和const成员函数,并且不允许其进行赋值操作,在这在一般庆跨年很少用到。

const修饰函数参数
传递过来的参数在函数内不可以改变(无意义,因为Var本身就是形参,可能也是检验吧)

void function(const int Var);

参数指针所指内容为常量不可变

void function(const char *Var);

参数指针本身为常量不可变(char *var也是形参)

void function (char *const Var)

参数为引用,为了增加效率同时防止修改。修饰引用参数时:

void function (const class &Var);//引用参数在函数内不可以改变
void function(const TYPE& Var);//引用参数在函数内为常量不可变

这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本,然后传递过去,而它直接传递地址,所以这种传递比按值传递更高效,另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性,且是不可见的,他段时间存在一个局部域中,所以不能使用指针,只有引用const传递能够捕捉到这个加过。

const限定符和static的区别

1.const定义的常量在超出其作用域之后其空间会被释放,而static定义的静态常量在函数执行后不会释放其存储空间。
2.static表示是静态的。类的静态成员函数、静态成员变量是和类相关的,而不是和类的具体对象相关的。即使没有具体对象, 也能调用类的今天成员函数和成员变量。一般类的静态函数几乎就是一个全局函数,只不过他的作用域限于包含他的文件中。
3.在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类的外部,通常在类的实现文件中初始化,如:double Account::Rate = 2.24;static关键字只能用于类定义体内部的声明中,定义时不能标示为static。
4.在C++中,const成员变量也不能在类定义处初始化 ,只能通过构造函数初始化列表进行,并且必须有构造函数。
5.const数据成员,只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没有被创建时,编译器不知道const数据成员的值是什么。
6.const数据成员的初始化只能在类的构造函数的初始化列表中内进行,要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static const。
7.const成员函数主要目的是防止成员函数修改对象的内容,即const成员函数不能修改成员变量的值,但可以访问成员变量。当成员函数时,该函数只能是const成员函数。
8.static成员函数主要目的是作为类作用域的全局函数。不能访问类的非静态数据成员。类的静态成员函数没有this指针,这导致:
1.不能直接存取类的非静态成员变量,调用非静态成员函数。
2.不能被声明为virtual。

class Test
{
public:
Test():a(0){}
enum {size1=100,size2=200};

public:
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。

其中关于static、const、static const、const static成员的初始化问题:

类里的const成员初始化

class foo
{
public:
	foo():i(100){}
public:
	const int i=100;//error 不能在类中初始化
	
};
//或者通过这样的方式来进行初始化
foo::foo():i(100)
{
}

类中的static变量是属于类的,不属于某个对象,他在整个程序的运行过程中只有一个副本,因此不能再定义对象时,对变量进行初始化,就是不能用构造函数进行初始化,其正确的初始化方法是:
数据类型 类名::静态数据成员名=值

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值