C++编程思想学习笔记---第八章 常量

本文详细探讨了C++中的常量概念,包括const值替换的优势、const的安全性、C与C++中const的区别。此外,还介绍了指针与const的结合使用,如指向const的指针和const指针,以及const在类中的应用,包括const成员函数、mutable关键字和volatile关键字的作用。
摘要由CSDN通过智能技术生成

第八章

常量概念(由关键字const表示)是为了使程序员能够在变和不变之间画一条界限。这在C++程序设计项目中提供了安全性和可控性


1、值代替

C语言用#define  BUFSIZE 100的宏定义方式来做值替换。好处是100的意义清楚,并且修改方便。有经验的程序员会把100加上括号,以防止某些因为逻辑不严谨造成的错误。但是因为预处理器只做些文本替代,它既没有类型检查概念,也没有该功能,所以这样的做法经常会产生一些问题。C++中用const值避免这些问题。具体做法是:

const int bufsize = 100;

这样就可以在编译时编译器需要知道这个值得任何地方使用bufsize。同时编译器还可以执行常量折叠(constant folding),也就是说,编译器在编译时可以通过必要的计算把一个复杂的常量表达式通过缩减简单化。这一点在数组的定义里显得尤为重要:

char buf[bufsize];
可以为所有的內建类型以及由他们所定义的变量使用限定符const。

1.1 头文件里的const

要使用const而非#define,同样必须把const定义放进头文件。C++的const默认为内部连接(internal linkage),即,const仅在const被定义过的文件里才是可见的,而连接时不能被其他编译单元看到。当定义一个const时必须给它赋值。除非用extern作出了清楚的说明:

extern const int bufsize;
关于const变量的内存空间问题:通常C++编译器并不为const创建存储空间,它把这个定义保存到它的符号表里。要进行存储空间分配的情况:1)如上使用了extern 2)取一个const的地址。因为extern意味着有几个不同的编译单元能够引用它,所以必须有存储空间。

1.2 const 的安全性
如果用运行期间的产生的值初始化一个变量而且知道在变量生命期内是不变的,则用const限定改变量是程序设计中的一个很好的做法。

关于聚合,如果编译一下代码

//C08: Constag.cpp
const int i[] = { 1, 2, 3, 4 };
float f[i[3]];
int main()
{

}
会发现第二行,float f[i[3]]报错。error C2057: 应输入常量表达式。当定义整型数组i的时候,实际分配了一块不能改变的存储空间。然后不能在编译期间使用它的值,因为编译器在编译期间不需要知道存储的内容。这样在定义float型数组f的时候,i[3]就不是一个常量或常量表达式,因为不能作为数组的下标。

1.3 const在C语言和C++中的不同

1)C默认const外部连接,C++默认是内部连接。什么意思呢?如果你写以下语句

const int bufsize;
int array[bufsize];
在C语言中,这是合理的。编译器会认为bufsize是一个外部变量(默认外部连接),这里只是一个声明而已。

如果在C++中这么写,则会报错,因为编译器找不到bufsize的定义,它不会去文件外部寻找bufsize的定义。如果想完成同样的事情,应该显式地表达出来,如下

extern const int bufsize;
int array[bufsize];

当然此处的bufsize仍然是一个声明,而不是定义。


2)在C++中const不必创建内存空间,而在C中,一个const总是需要创建一块内存空间。

如果仅把const用来作值替换,则不需要创建内存空间(如同#define一样)。

如果对其取了地址,则会创建内存或者定义为extern也会创建内存。

下面再来区分几种const的写法:

extern const int x;    表示x是一个外部变量,这里实在引用声明。

extern const int x = 1;     表示x是在当前定义的一个变量,并且声明为extern,可以被其他文件引用,并且强制分配内存

他们的区别仅在于是否初始化。


2、指针

2.1指向const的指针

const int* u;

u是一个指针,它指向一个const int。这里不需要初始化,因为u可以指向任何标示符,该指针本身并不是一个const,它可以被任意更改,但被指向的地址中的内容是不能更改的。

int const* v;

可能很多人以为它应该是标示一个指向int的const指针,但实际上它跟const int* u一样。


2.1.2const指针

int d = 1;

int* const w = &d;

w是一个指针,这个指针指向int的const指针,它现在不能指向其他地方了,所以必须要初始化。然后该地址中存放的内容是可变的。*w = 2;

再来两个例子,我们看看到底怎么区分常指针和常变量。

int d = 1;

const int* const x = &d;  x是一个常指针,同时它指向一个常变量

int const* const x2 = &d; x2也是一个常指针,同时指向一个常变量

所以看出来怎么区分了吗?如果const挨着指针名称写,它就是一个常指针,否则就只是修饰所指向的变量。

注意:可以把一个非const对象的地址赋给一个const指针,因为有时不想改变某些可以改变的东西;但是不能把一个const对象的地址赋给一个非const指针,因为这样做可能改变该const对象的值。


3、类

3.1类里的const

考虑这样一种情况,程序员需要像#define一样定义一个数组大小size,他使用了一个变量const int size; 但是这个数据成员是类里面的,需要在构造函数里初始化。而另一个需要它的数组array[size]却并不一定在size之后初始化(虽然这完全可以由程序员的细心做到),这样肯定会出错,因为此时编译器并不知道size是什么。为了防止这种情况,C++使用了一个叫做“构造函数初始化列表”的东西来优先初始化一些数据,这些赋值操作比任何构造函数体里面的语句都先执行,而这里也正是const类型初始化的地方。

class Fred
{
	const int size;
public:
	Fred(int sz);
	void print();
};

Fred::Fred(int sz) : size(sz) {}
void Fred::print() { cout << size << endl; }
在构造函数后面加一个冒号,后面跟着数据成员。不难发现,这跟继承的语法有点相似。可以把size(sz)看成是基类的构造函数,sz是它的参数。实际上,这叫做 內建类型的构造函数


3.2编译期间类里的常量

上面才说了,const量必须在构造函数的初始化列表里优先初始化,这里又说有一种const量可以而且必须在定义时赋值

让一个类在编译期间就有常量,那这个变量必定是属于整个类的,而不是属于某个对象的。可以推到出,它必定要用关键字static修饰。对之前的代码稍加修改

class Fred
{
	static const int size = 100;
	int array[size];
public:
	Fred(int sz);
	void print();
};
这样,在编译期间,size的值就是100。另外,在老的C++代码中,static const并不被支持,通常用一种不带示例的无标记枚举enum来代替,因为一个枚举在编译期间必须有值,它在类中局部出现,而且它的值对于常量表达式是可以使用的。以下代码功能相同

class Fred
{
//	static const int size = 100;
	enum { size = 100 };
	int array[size];
public:
	Fred(int sz);
	void print();
};

3.3const对象和成员函数

如何定义一个const成员函数?

必须在声明和定义中同时加上const修饰符,再次强调,必须同时。

这样做有什么用?

一个const成员函数不管以什么方式改变一个非const数据成员变量(const本身无论在何处都不能更改,讨论没有意义)或调用非const成员函数,编译器都会报错。

class Fred
{
	int i;
public:
	Fred(int ii);
	int f() const;
};

Fred::Fred(int ii) : i(ii) {}
int Fred::f() const { return i; }

3.4mutable关键字

继续打脸!上面才说了,const成员函数保证了不能对数据成员进行修改,同时不能调用其他非const成员函数。现在问:如果确实需要在const函数里改变某个变量改怎么办?

答案是:关键字mutable

class Fred
{
	mutable int i;
public:
	Fred(int ii);
	int f() const;
};

Fred::Fred(int ii) : i(ii) {}
int Fred::f() const { return ++i; }
现在可以对i为所欲为了。类的用户可从声明里看到哪个成员能够用const成员函数进行修改


3.5volatile关键字

与const相反,volatile表示在编译器的认识范围以外,这个数据可以被改变

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值