C++中的const的使用详解

所属分类: 软件编程 / C 语言 阅读数: 82
收藏 0 赞 0 分享

const的基本使用

const的用法我觉得对于一个以后想着做一个优秀的程序员来说,这是必须熟练掌握的技能。因为网上有好多的文章介绍它的写的非常好,有的我就直接拿过来了~,现在我们来看看他的用法。

const 要求他所修饰的对象为常量,不可被改变,不可被赋值,不可作为左值.

1、函数体内修饰局部变量

例:

void func(){
const int a=0;
}

const作为一个类型限定词,和int有相同的地位。

const int a; int const a;

是等价的。于是此处我们一定要清晰的明白,const修饰的对象是谁,是a还是int
const要求他所修饰的对象为常量,不可被改变,不可被赋值,不可作为左值(l-value)。所以很明显它修饰的是a。这是一个很常见的使用方式:

const double pi=3.14;

在程序的后面如果企图对pi再次赋值或者修改就会出错。然后看一个稍微复杂的例子。

const int* p;

因为int* p;和 int *p;是等价的。
所以const int (*p)和int const (*p)是等价的。现在一目了然const 修饰的是谁? 是*p.所以p+=1;是合法的

*p+=1;是非法的因为const修饰了你。

int* const p;那这个什么意思?

看const修饰的是什么? 它修饰的p。但是p是一个int型的指针,所以这个指针的地址没有办法修改。

p+=1; //这就是非法的
*p+=1; //这个是合法的

再看一个更复杂的例子,它是上面二者的综合

const int* const p;说明p自己是常量,且p指向的变量也是常量。
于是

p+=1; //非法
*p+=1; //非法

const 还有一个作用就是用于修饰常量静态字符串。例如:

const char* name=David;

如果没有const,我们可能会在后面有意无意的写name[4]='x'这样的语句,这样会导致对只读内存区域的赋值,然后程序会立刻异常终止。有了 const,这个错误就能在程序被编译的时候就立即检查出来,这就是const的好处。让逻辑错误在编译期被发现。

2、在函数声明时修饰参数
举个例子void * myMemMove(void *dst,constvoid *src,intcount )这是我写的memmove函数的声明,这个函数的意思就是(任意类型)把*src的内容复制给*dst,我们现在很明显的看到*src它只让你复制,你不能修改它的值,所以怕你在以后的函数的定义里出现问题现在在声明里限制你。

3、全局变量
我们的原则依然是,尽可能少的使用全局变量。我们的第二条规则 则是,尽可能多的使用const。如果一个全局变量只在本文件中使用,那么用法和前面所说的函数局部变量没有什么区别。如果它要在多个文件间共享,那么就牵扯到一个存储类型的问题。

有两种方式。

1.使用extern
例如
/* test.h */
extern const double pi;
/* test.c */
const double pi=3.14;

然后其他需要使用pi这个变量的,包含test.h

#include test.h

或者,自己把那句声明复制一遍就好。

这样做的结果是,整个程序链接完后,所有需要使用pi这个变量的共享一个存储区域。

2.使用static,静态外部存储类

/* constant.h */
static const pi=3.14;

需要使用这个变量的*.c文件中,必须包含这个头文件。

前面的static一定不能少。否则链接的时候会报告说该变量被多次定义。这样做的结果是,每个包含了constant.h的*.c文件,都有一份该变量自己的copy,该变量实际上还是被定义了多次,占用了多个存储空间,不过在加了static关键字后,解决了文件间重定义的冲突。坏处是浪费了存储空间,导致链接完后的可执行文件变大。但是通常,这个,小小几字节的变化,不是问题。好处是,你不用关心这个变量是在哪个文件中被初始化的。
其实const我觉得更多是程序员自己限制自己,自己告诉自己后面哪里不能出现错误

举个例子吧。

#include<stdio.h>
#include<Windows.h>
int main()
{
	int *p;
	const int a = 0;
	p = &a;
	*p = 3;
	printf("a= %d \n", a);
	system("pause");
	return 0;
}

现在看看运行结果

现在我要说一个const操作里面比较骚的一些做法,

举个例子我们以前写过的一个类,我们会使用operator[]来返回一个reference的指向,这个一般情况我们都会写一个

const的也会写一个非const的opeartor[].这是我们最常见的一个代码:

T& operator[](int position) 
{ 
 return xxx[position]; 
} 
const T& operator[](int position) const 
{ 
 return xxx[position]; 
}

这是我们平时写的初级的代码,但是现在当我们要写一个TextBlock内的opeartor[]不单只返回一个referencr了,也可能执行边界检查,日志访问信息,还有什么数据完善性检验等等一大堆繁琐的代码,这个时候当你实现operator[] const和operator[]() const,的时候两份代码大部分都一样,这里伴随的是代码重复,编译时间变长,维护代码膨胀等等头疼的问题. 当然啦,你可以让上述那些繁琐的函数全部封装的别的函数中,然后分别在operator[]()和operator[]()const当中调用但是你还说重复了一些代码比如两次return语句,函数调用.真正该做的是实现operator[]的机能一次并使用它两次。也就是你只需要写一个函数,令另外一个调用这个,这促使我们将常量性转移. 接下来 见证奇迹我们来看看下面这个代码是怎么实现的上述的操作的:

class TextBlock 
{ 
public: 
 ... 
 const char& operator[](std::size_t position) const 
 { 
 ... 
 ... 
 ... 
 return text[position]; 
 } 
 
 char& operator[](std::size_t position) 
 { 
 return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]); 
 } 
};

来仔细看这个操作;return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
首先把*this强制转换为const TextBlock,再然后调用const的operator[],最后再把const的operator[]的返回值的const常量性取消,然后返回一个非const的值. 这里的调用实在是太妙了,我们可以思考一下,好好想想这里的深意.
但是会有人说,为什么不用const operator[]调用operator[]呢,这样强制两个都可以行的通啊.这样想是错的!

令const版本调用调用no-const版本以避免重复并不是你该做的事情. 记住const所修饰函数的承诺就是我绝对不会修改你,no-const函数可没有这种承诺,所以你让一个const函数去调用一个no-const函数是不现实的. over其实const有很多可以玩的属性,只要我们想到就可以去实现,这里就说这么一个就ok. 接下来我们来瞧瞧另外两个关键字.

 C++中的const的使用详解

const在c/c++中还是会经常出现的,并且如果不理解const会在编程出现的错误而不知所措,无法理解。下面从几个角度简要理解const的内容,应该还是蛮有用的。

const与指针类型

const int*p = NULL; 和int const*p = NULL;是等价的。因为const都在“ * ”的前面,其实是以*为标志的。

1. int x = 3; const int *p = &x; 


// p = &y;正确 , //*p = 4;错误 

指针其实一般是4个字节长度。p的内容是存储0x….,就是其他数据的地址。因此这里的const修饰*p就是说:*p的内容是不可直接被赋值改变的。

而p本身存储的地址是可变的,可以变成其他的0x…..当p指向其他的数据地址时,*p也就随之变成新的数据。

int x = 3; int *const p = &x; //p = &y是错误的

总结:其实是看这个const是在的前面还是后面,如果在的前面,则表示修饰的是整个“ p ”,而在后面,则表示修饰的是只有p。

显然有:在前面,则表示整个*p是const的,因此p可以指向其他的地址,而*p则是const的,无法改变。同理,int *const p = &x;则表示指针p是固定的,就是说p指针存储的地址是固定的,其内容是const,因此无法修改为其他值(即指向其他地址)。

const与引用

int x = 3; const int &y = x; 
//x = 10;正确 //y = 20; 错误 

引用前面有const,所以不能通过y来修改x的值。

本人的理解:const int &y就是相当于const int *y1 = &x;然后y = 20就相当于*y1 = 20;这显然时不允许的(就如前面所说的,*p时const,无法直接赋值给*p)。因为引用就相当于给x取了一个别名y,此时y不就是y1指针所指的内容吗?也就是说上面的例子:y = 20;就是相当于 *y1 = 20.

错误的const使用

其他:const int x = 3; int *y = &x; 不能通过编译。因为可能通过*p修改本应该是const的x;
int x = 3; const int *y = &x; 这是可以的,这里的x是可变的,通过由于*y是const的,因此*y就是只能是读取x的值,而不具有写入x的权利。

总结:可以说是只能把一个东西权限缩小,而不能使其原始的权限增大。

const在函数中的应用

主要是防止函数的误操作,对值进行更改

void fun(const int&a, const int&b) 
{ 
//这里就不能对a或b进行更改,否则会编译出错 
}

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多