const可能是C++最受欢迎的描述字了。他提供了更好的安全语义。很多企业在面试的时候经常抛出诸如“请谈谈const的用法”之类的题目。
const描述字允许我们提供一个语义约束:声称其被描述的对象具有“不该被改动”的性质,令人感到兴奋的是,编译器会强制实施这项约束。同时,他也允许告诉编译器和其他程序员,某些值应该保持不变。const多才多艺,在许多语境下可以用到他:变量、指针、对象、参数、返回值甚至函数本身。现在把const在C/C++中的用法作一个总结。
1.C中的const
在C中的const主要用于描述变量、指针以及函数声明,我们一步步来。
1.1 变量
以下是const修饰变量时的常用用法:
cpp 代码
- const int SIZE = 1024;
- const double PI = 3.1415926;
<o:p></o:p>
他为程序引入一个常量,以防止出现太多的“Magic Number”,同时也是为了能够方便的维护。在C中,我们也可以用#define来达到同样的目的:
cpp 代码
- #define SIZE 1024
- #define PI 3.1415926
不过其中存在一些微妙的区别,以至于我们强烈推荐使用const而不是#define来引入一个常量。原因如下:
#define语句是一个预处理命令,他在编译器对源代码进行编译之前,就把所有的“名称”用“值”所替换了。所以,我们的程序中,其实并没有包括SIZE或PI这样的字,而是1024和3.1415926,因此,也更加不会有SIZE或PI的类型信息。所以,概括来说,用#define来引入常量有两点缺陷:
1. 我们所使用的名称没有被引入符号表(symbol table);
2. 他抛弃了名称的类型信息;
第一点会在调试、维护阶段添不少麻烦。可以说,用#define仅仅屏蔽了“开发期”的Magic Number。而第二点,部分的放弃了C++蕴含的严格的类型检查机制所带来的种种益处。综合考虑,我们强烈推荐在C/C++中用const替代#define。
此外,还有一个传说中的“the enum hack”,用一个无名的enum来引入常量。其用法是:
<o:p> </o:p>
cpp 代码
- enum { SIZE = 1024, MAX = 4096 };
从某方面来讲,enum与#define有些相似,有时候这是必须的。取enum或者#define常量的地址是不合法行为,但取const常量的地址就是合法的。如果你不想别人取得你常量的指针或者引用,同时你也不希望与#define带来的缺点进行某种“妥协”,那么enum会是一个很好的选择。我们应该认识enum hack的动力还来自于,很多现存的C++代码都大量的使用了这个技巧,我们应该认识他们。
1.2 指针
指针从某方面来说也是一种变量,所以上一小节所述同样也适用。不过还是有两点要注意:
首先指针蕴含两方面内容:指针本身以及所指对象,可以针对这两部分分别使用const描述字:
cpp 代码
- char greeting[] = “hello”;
- char * p = greeting;
- const char * p = greeting;
- char * const p = greeting;
- const char * const p = greeting;
同时,还应该注意到,const char *和char const *并没有什么不同,个人习惯罢了。虽然变化多端,却并不高深莫测:星号右边的const描述指针本身;星号左边的const描述所指对象。
第二点,由于常量指针经常用于引入常量字符串,所以我们在头文件中经常看见上述用法。不过,这并不是一个好想法。如果我们的想法确实是引入字符串常量,请尝试使用:
<o:p> </o:p>
cpp 代码
- const std::string GREETING(“hello”);
1.3 函数声明
const最具威力的用法就是在函数声明时的应用。
关于const参数,没有什么特别新颖的观念。唯一需要注意的就是,尽量在不会被修改的参数前面加上const。只不过多打6个字母,却可以在以后很长时间内,省下那些恼人的错误。这也是C++中为数不多的,很“便宜”的好处,尽量的使用吧!如果还想深入一点,就需要注意以下问题:由于种种原因,利用const来区分“需要改动”和“不需要改动”的参数,并不是一种“万能公式”。幸好,经过我们精心修改以后的“公式”,在大多数场合都是适用的:
1. 如果参数是内置类型,请使用Type *和Type来加以区分;
2. 如果参数是用户自定类型,并且具有较复杂的构造-析构函数,请使用Type &和 const Type &来加以区分;
关于返回值,也差不多这样:返回一个内置类型的时候,按值返回就好了;当返回一个用户自定义类型时,最好采用const Type。原因是:良好的自定义类型应该避免无端的与内置类型不兼容。而改动内置类型返回值是不合法的,所以,我们也最好为我们自己的类型提供这个“安全锁”。这样,往往可以降低因客户错误而造成的意外,而又不至于放弃安全性和高效性。
2.面向对象中的const
这一章我们从前面一章的内容过渡到成员函数上,然后最后讨论以下有关bit const和logical const的相关概念。
2.1 过渡
第一章的内容大部分都可以适用于这一章。但有两点需要注意:
首先,关于常量初始化的问题。因为const变量一旦定义后即不可更改,所以在global域或者namespace域中使用const常量,通常(也必须)把声明和定义放在一起。在面向对象部分则不能这么干。因为类定义不可以对数据成员进行初始化(虽然有些编译器允许),所以就要另外找个地方对const数据成员进行初始化。构造函数?不。按照构造函数的语义,他是在已经“创造”了一个对象后,才进行的一些初始化操作。而按照用户自定义类中的常量语义,他应该在此之前就存在。所以应该(事实上也必须)在成员初始化列表中对数据常量进行定义。
cpp 代码
- class A {
- const int MAX_SIZE;
- int* arr;
- public:
- A(void)
- : MAX_SIZE(1024),
- arr(new int [MAX_SIZE] ) {
- for(int i=0; i
- arr[i] = i;
- }
- ~A() {}
- void print(void) {
- for(int i=0; i
- std::cout << arr[i] << std::endl;
- }
- };
这样做有着清晰却复杂的语法。所以另外一种妥协方式就是“the enum hack”:
cpp 代码
- class A {
- enum { MAX_SIZE = 1024 };
- int* arr;
- public:
- A(void)
- : arr(new int [MAX_SIZE] ) {
- for(int i=0; i
- arr[i] = i;
- }
- …
- };
简洁明了,这也是为什么现有代码大量使用这种技术的原因。不过他也有个显见的缺点:不能引入除整型以外其他类型的常量。
2.2 const成员函数
const还可以修饰类的成员函数。
像函数参数一样,类的成员函数也可以划分两类:修改数据成员和不修改数据成员。当我们声明一个成员函数的时候,编译器会默认其为修改数据成员的函数,我们称这种为non-const成员函数(non-const member function),而与之对应的称为const成员函数(const member function)。同时,C++规定,对于用const声明的类对象如:
<o:p> </o:p>
cpp 代码
不能调用其non-const成员函数,而只能调用const成员函数。也就是说如果我们不采取一些措施来通知编译器,那么按默认规则,test_A将不能调用任何成员函数(构造和析构函数除外)。
cpp 代码
- const A test_A;
-
-
- test_A.print();
改善C++程序效率的一个根本办法是以const Type &来传递对象,而这技术可行的前提是可以调用const对象的const成员函数。所以在设计类的成员函数的时候,我们就一定要明确区分那些是const成员函数,哪些是non-const成员函数,并且在const成员函数的参数列表和分号之间,使用const描述字,例如class A中:
cpp 代码
- class A {
- …
- void print(void) const {
- for(int i=0; i
- std::cout << arr[i] << std::endl;
- }
-
- };
-
- int main(void) {
- const A test_A;
- test_A.print();
- return 0;
- }
同时注意,如果成员函数的常量性不同可以被重载。例如:
cpp 代码
- class A {
- …
- void print(void){
- for(int i=0; i
- std::cout << arr[i] << std::endl;
- }
- void print(void) const {
- for(int i=0; i
- std::cout << arr[i] << std::endl;
- }
- };
-
- int main(void) {
- const A const_A;
- A non_const_A;
- const_A.print();
- non_const_A.print();
-
- return 0;
- }
分享到:
相关推荐
数据是信息的载体,是描述客观事物的数、字符、符号的集合,可以是数字、文字、图像等各种形式。信息则是数据在人们头脑中的反映,是对现实世界事物的理解和认知。数据是信息的基础,而信息是对数据的解读和处理后的...
通过阅读“数据结构C++语言描述.pdf”这本书,读者可以深入学习这些概念,并通过实践加深理解。 总之,数据结构是编程的基础,而C++作为一门强大的编程语言,为我们提供了丰富的工具来实现和操作这些结构。熟练掌握...
### 一个C++描述的栈类 #### 概述 本文档介绍了一个使用C++编写的通用栈类的实现。该栈类通过模板机制支持多种数据类型,并且提供了丰富的功能来管理和操作栈内的数据。栈是一种后进先出(Last In, First Out, LIFO...
在C++编程中,设计一个...总之,用C++描述分数类并进行各种运算涉及类的定义、成员变量、构造函数、运算符重载以及友元函数等多个C++核心概念。通过这样的实现,我们可以有效地处理分数,使代码更加模块化和易于维护。
《C++语言const关键字使用方法图文详解》 在C++编程中,`const`关键字扮演着重要的角色,它用于定义常量和限定变量的可变性。...正确理解和使用`const`能够提升程序的安全性和效率,也是C++程序员必备的基本技能之一。
- 描述了如何正确使用const关键字,包括const成员函数和const参数,以及const和非const指针的区别。 - 解释了引用作为函数参数和返回值的优势,以及如何避免引用未初始化的问题。 4. **运算符重载**: - 讨论了...
函数模板是C++模板技术的核心组件之一,它允许开发者定义泛型函数,以便在不同的数据类型上实现相同的操作。实验中,要求设计一个函数模板sort,采用选择法排序方式对数据进行降序排序。sort函数模板的设计方法可以...
在本例中,`TestSFunc1`函数的参数`obj`被声明为`const SBASET*`类型,这意味着通过`obj`只能访问`SBASET`类及其派生类中的`const`成员。这是理解为什么`TestSFunc1`函数内部调用`obj->fool()`时会调用到基类的`fool...
总结来说,这个“数据结构(C++描述)--复数类”的项目展示了如何在C++中利用面向对象编程来实现复数的数学运算,包括加减乘除以及输入输出操作。这不仅加深了对C++语言的理解,也强化了数据结构和算法的应用能力,...
### 数据结构与算法分析 C++描述 #### 一、引言与重要性 在软件开发领域,掌握数据结构和算法是构建高效、可维护及可靠软件系统的关键。本篇文章将根据给定的文件内容,深入探讨《数据结构与算法分析 C++描述》一...
16. P65 1 行错误:word(字)机器上的自然的整型计算单元更正:word(字)是在给定机器上进行整型计算的自然单元 知识点:这里的修订是为了使语言更加准确和通俗。在 C++ 中,word 是一种基本数据类型,而这里的...
根据给定文件中的四个C++小题目,我们可以逐一分析这些代码背后的逻辑以及它们解决的问题。 ### 第一个小题目 **题目描述:** 一个球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10次...
根据给定的文件信息,以下是对C++基础知识的详细总结,涵盖了标题与描述中的关键知识点,包括虚函数、常量成员函数、构造函数、析构函数、模板等核心概念。 ### 虚函数与常量成员函数 在C++中,虚函数允许派生类...
- 变量名、函数名、类名等应清晰、简洁且具有描述性,避免使用缩写和无意义的字符。 - 类名首字母大写,如`ClassName`;变量和函数名小写,单词间用下划线连接,如`variable_name`或`function_name`。 - 常量命名...
- **国际标准结构**:描述了整个C++ 14标准文档的组织结构。 - **语法表示法**:解释了C++ 14标准中使用的语法表示方法。 - **C++内存模型**:详细介绍了C++中的内存管理机制,包括内存分配、释放、同步等。 - **C++...
1. **数据存储**:矩阵的数据通常以二维数组的形式存储,可以是静态数组(固定大小)或动态分配的数组(大小可变)。静态数组在编译时确定大小,而动态数组在运行时根据需求分配空间。 2. **构造函数**:矩阵类应...