Cpp:const、引用、typedef和enum

一、const


考虑问题:下面程序中数字 10 的含义?

for (int i = 0; i != 10; ++i){
    //...
}
  • 显然从程序来看,我们并不知道 10 是怎样一个存在,因此它也被称为 魔数(magic number),意思是这个数的意义不能从上下文体现出来,就像魔术一样凭空出现了。

  • 或者如果这个程序多次使用数字 10 的话,当需要要把 10 改为 20 的话就麻烦了。

一种简单的解决办法是

int max = 10;
for (int i = 0; i != max; ++i){
    //...
}

这样不仅可读性好很多,而且修改起来也相当方便。

max 是可以被修改的

1.1、定义const对象

上面到,max 可能会被有意或无意的修改,在某些情况下这很严重。const 提供一种解决办法

const int max = 10;

此时定义 max常量 并初始化为 10,但此时的 max 不可被修改。

C++中的 const 跟 Java 中的 finalPHP 中的 define 类似。
const 定义的常量不可修改,所以定义时必须初始化:

const std:string hi = "Hello";//正确
const int i;    //错误 

1.2、const对象默认为文件的局部变量

跟普通的变量不一样,const定义的变量需要特别的说明才可以在其他文件中访问。例如

  • 普通变量

      //file1.cpp
      int max = 10;        //定义变量
    
      //file2.cpp
      extern int max;    //声明外部变量
      //下面可以使用变量 max
    
  • const变量

      //file1.cpp
      extern const int max = 10;    //定义变量
    
      //file2.cpp
      extern const int max;        //声明外部变量
      //下面可以使用const变量 max
    

定义非 const 变量时默认问 extern。而 cost 变量必须显式的指定它为 extern 才可以被其他文件访问。

二、引用


引用(reference)就是对象的另一个名字。在实际程序中,引用主要作为函数的形参,形参的内容将在后续介绍。

引用是一种 复合类型(compound type),在变量名前加 & 符号来定义。复合类型是指用其他类型定义的类型。每个引用都关联到其他某一类型,不能定义引用的引用,但可以定义任意类型的引用,并且可以有多个引用。下面用例子说明

int val = 1;
int &reVal1 = val;    //正确
int &reVal2 = val;    //正确:val的第二个别名

int &reVal3;        //错误:引用必须初始化
int &reVal4 = 1;    //错误:引用必须使用对象初始化

2.1、引用是别名

引用只是它所绑定对象的另一个名字,在引用上做的所有操作实际上都作用在原对象上:

reVal1 = 5;            //相当于:val = 5;
reVal1 += 1;        //相当于:val += 1;
int val2 = reVal1;    //相当于:int val2 = val;

下面程序的结果更能直观的说明这个问题

int main()
{
    int val = 0;
    int &reVal1 = val;
    int &reVal2 = val;
    std::cout << "val = " << val << ", reVal1 = "<< reVal1 << ", reVal2 = " << reVal2 << std::endl;

    reVal1 = 1;    //对 reVal1 操作
    std::cout << "val = " << val << ", reVal1 = "<< reVal1 << ", reVal2 = " << reVal2 << std::endl;

    reVal2 = 2;    //对 reVal2 操作
    std::cout << "val = " << val << ", reVal1 = "<< reVal1 << ", reVal2 = " << reVal2 << std::endl;

    return 0;
}

运行结果是:

val = 0, reVal1 = 0, reVal2 = 0
val = 1, reVal1 = 1, reVal2 = 1
val = 2, reVal1 = 2, reVal2 = 2

2.2、const引用

非 const 不能引用 const,只有 const 能够引用 const,且 const 引用不能修改:

int i = 0;
const int val = 0;            //正确:0是右值
const int &reVal1 = val;    //正确:val是左值
const int &reVal2 = i;        //正确

reVal1 = 1;                    //错误:const 引用是只读的
int &reVal3 = val;            //错误:非 const 不能引用 const

跟非 const 不同之处还有:

int i = 0;
const int &re1 = 1;            //正确
const int &re2 = re1 + i;    //正确

对于下面这种绑定到同一类型的情况:

int i =  0;
const int &re1 = i;    //此时 re1 = 0;
i = 1;                //此时 re1 = 1;

当改变 i 时,re1 也会被改变。

还有另一种情况:绑定到不同类型的值

double i =  1.1;
const int &re1 = i;    //此时 re1 = 1;
i = 2.1;            //此时 re1 = 1;

因为对于这种情况,编译器会解释为:

int temp =  i;
const int &re1 = temp;

所以,改变 i 实际上改变的是 tempre1 不受影响。

const 引用可以绑定左值或右值,非 const 引用只能绑定左值。

三、typedef


typedef 跟引用有点类似,不过 typedef 是定义类型的别名:

typedef double wages;    //wages 是 double 的别名
typedef int score;        //score 是 int 的别名
typedef wages salary;    //salary 是 double 的别名

其作用是:

  • 为了隐藏特定的类型,强调使用类型的目的;
  • 简化复杂的类型定义,易于理解;
  • 允许一个类型用于多个目的,并且每次使用时目的明确;

四、枚举


如果要为某属定义一组可选择的值,每个只对应一种状态,比如文件的打开状态:输出,输入和追加分别对应 0,1,2。则有可能会这样定义:

const int input = 0;
const int output = 1;
const int append = 2;

属性选择很多时这样定义就不方便,枚举(enumeration) 是一种替代方法。

4.1、定义和初始化枚举

枚举的关键字是 enum,定义如下

enum open_modes {input, output, append};

默认地,第一个枚举成员赋值为 0,后面的依次加 1。

4.2、枚举成员是常量

可以为一个或多个成员提供初值,初始化枚举成员的值必须是一个 常量表达式(constant expression),整型字面值也是常量表达式。

// sphere = 2, polygon = 2
enum forms {shape = 1, sphere, cylinder = 1, polygon}

4.3、每个enum都定义了一种唯一的类型

forms f1 = shape;    //正确:shape 是 forms 类型的枚举成员
forms f2 = square;    //错误:square 不是 forms 类型;
forms f3 = 1;        //错误:1 是 int类型,不是forms类型

即使 1 与 shape 相关联,但是 1 赋值给 f3 还是非法的

五、类类型


在C++中可以通过定义 类(class) 来自定义数据类型。类定义了该类型的对象包含的数据和该类型对象可以执行的操作。标准库类型 string、istream、ostream都定义成类,关于类后续详细介绍。

本节继续使用在 读《C++primer》day1:快速入门 的第三部分提到的 Sales_item 类举例。

5.1、从操作开始设计类

每个类都定义了一个 接口(interface) 和一个 实现(implementation)。接口由使用该类的代码需要执行组成,实现一般包括该类所需的数据及操作。

定义类时,通常先定义接口,即该类所提供的操作。以 Sales_item 举例:

  • 加法 + :将两个对象相加;
  • 输入 >> : 读取一个对象;
  • 输出 << :输出一个对象;
  • 赋值 = :将一个对象赋给另一个对象;
  • 对比 ? :对比两个对象是否属于同一本书(函数same_isbn);

虽然现在我们并不能实现这些操作(需要更多的知识),但可以考虑实现这些操作需要什么样的数据:

  • 记录各书本的销售册数;
  • 该书的总销售收入;
  • 计算该书的平均售价;

大概就可以知道需要一个 unsigned 类型对象来记录数的销售册数,一个 double 类型对象计入总收入,string 类型对象记录书本的ISBN。

5.2、定义Sales_item类

按上一节提到的操作和数据,可以这样定义:

class Sales_item {
public:
    //各种操作在此定义
private:
    std:string isbn;
    unsigned units_sold;
    double revenue;
};

类定义以关键字 class 开始,后面是该类的类名,类体位于花括号内部,花括号后面必须要跟一个分号。

新手经常会忘记类定义后面的分号!

类体可以为空,类体定义了该类型的数据和操作。这些数据和操作也称为 成员(member),数据称为 数据成员(data member),操作称为 成员函数

类定义可以包含多个 private 和 public 访问标号(access label),给定的访问标号作用域到下一个访问标号出现时为止。类中 public 部分定义的成员在程序的任何部分都可以访问,不是类的组成部分的代码不能方便问 private 成员,这样可以保证Sales_item对象不能直接操纵数据成员。

5.3、使用struct关键字

struct 关键字也可以定义类类型,它是从 C 语言继承而来。区别

  • 用关键字 class 定义类:定义在第一个标号之前的所有成员都默认为private;
  • 用关键字 struct 定义类:定义在第一个标号之前的所有成员都默认为public;

struct 重新定义前面的Sales_item类:

class Sales_item {
//不需要 public 访问标号
private:
    std:string isbn;
    unsigned units_sold;
    double revenue;
};

用 class 和 struct 关键字定义类的唯一区别就在于 默认访问级别。默认情况下,struct 的成员为 public,而 class 的成员的是 private。

END.


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 307、setValue:forKey和setObject:forKey的区别是什么? 答:1, setObjec...
    AlanGe阅读 1,521评论 0 1
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,766评论 1 10
  • (1)可以定义 const 常量 (2)const 可以修饰函数的参数、返回值. 详细内容: 1、什么是const...
    幽鬼09阅读 705评论 0 4
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 一位家长向我求助:武校长,晚上好!我家小孩算是一个勤快,也很自觉的孩子。老师布置的作业,回家第一时间做完。剩下时间...
    武际金阅读 2,702评论 0 16