大二寒假读书笔记150125

来源:互联网 发布:森田药妆面膜 知乎 编辑:程序博客网 时间:2024/04/20 23:54

1、接着写昨天未完的初始化问题,这个十分重要,内置类型,类类型什么乱七八糟的,也是笔试经常考的

内置类型变量是否自动初始化取决于变量定义的位置。在函数体外定义的变量都初始化为0,函数体里定义的内置类型变量不进行初始化。记住,是内置类型变量;还有

永远不要依赖未定义行为!

类类型的初始化由构造函数来实现,程序猿如果不自己写构造函数就会调用默认构造函数。

2、声明和定义。

使用extern关键字声明变量名,不是去定义它。声明的变量在其他的文件中已经定义过了。定义的同时就相当于已经在本文件中声明了。soooo,定义只有一次!声明可以有很

多次。

<span style="white-space:pre"></span>extern int i;//声明但未定义<span style="white-space:pre"></span>int i;//声明又定义
当声明也是定义时,声明才可以有初始化式,因为只有定义才分配存储空间。

<span style="white-space:pre"></span>extern double pi = 3.1415926//定义并初始化,只有当extern声明位于函数外部是,才可以含有初始化式
因为已经初始化的extern声明被当作是定义,所以该变量任何随后的定义都是错误的(一个废话,变量当然不能定义两次)

extern double pi = 3.1415926;//含有初始化式的extern被当作定义并初始化extern double pi;//这仅仅是声明,正确double pi;//重复定义,错误extern double pi = 3.1415926;//重复定义,错误
任何在多个文件中使用的变量都需要有与定义分离的声明。在这种情况下,只有一个文件含有变量的定义,其他文件则包含变量的声明

3、作用域:用来区分名字的不同意义的上下文称为作用域(特别通俗易懂有没有)

就是说同一个变量名字可以重复在不同的作用域里。

#include<iostream>int main{    int sum = 0;    for(int val = 1;val <= 10;++val)         sum +=val;std::cout<<"Sum of 1 to 10 inclusive is"              <<sum<<std::endl;return 0;}
这个程序定义了三个名字,使用了两个标准库的名字。一个名字为main的函数,以及两个名字为sum和val的变量。

定义在函数外部的名字具有全局作用域,在程序的任何一个地方访问。

定义在某一个函数中的名字在整个函数都可以访问,在函数外不能访问。比如sum具有局部作用域

定义在某个语句中的变量具有语句作用域,比如val,出了for语句就不能使用。

再来看一段代码,讲的是作用域的嵌套

#include<iostream>#include<string>std::string s1 = "hello";//s1为全局变量int main{    std::string s2 = "world";//s2为局部变量//输出hello worldstd::cout<<s1<<" "<<s2<<std::endl;int s1 = 42;//s1是局部变量,并且此时屏蔽(hide)了上面的全局变量s1//输出42 worldstd::cout<<s1<<" "<<s2<<std::endl;return 0;}
变量从声明开始才可见,在执行第一次输出是用的s1是全局变量;到了第二次输出的时候用的是局部变量。


4、const限定符。这个太重要了,细节决定成败啊,是读康斯特还是啃斯特?

首先我们要知道为什么要用到const,我觉得把它仅仅理解为变量常量化是不好的,先放段代码

for(int index = 0;index != 512;++index){    //.....}
首先是可读性,里面凭空出来个512是怎么回事?512有什么含义吗,除了汶川地震之外?本例中512被称为魔数(magic number),它的意义在上下文中没有体现出来。

其二是可维护性,假如程序非常庞大,512出现了100+次,有80次是表示某一缓冲区的大小,剩余的次数用于其他的目的。现在我们需要把缓冲区大小增大到1024,你懂的:)

要实现这一改变,必须检查每个512出现的位置。我们必须确定哪些512代表缓冲区的大小,改错一个就会引起程序崩溃。

这时,你理所应当的想到定义一个变量表示,不错不错。

int bufSize = 512;for(int index = 0;index != bufSize;++index){    //....}

然而此时又出现一个问题,你定义的这个bufSize是可以被修改的,它可能被有意无意的修改。此时就要用到const限定符,它可以把一个对象转换成一个常量

const int bufSize = 512;//const对象

此时变量bufSize仍然是一个左值,但是现在这个左值是不可修改的,尝试修改或导致编译错误,并且常量在定义后就不能被修改,所以定义式必须初始化。

const std::string hi = "hello!";//initializedconst int i, j = 0;//error:unitialized

注意:与其他变量不同,除非特别说明,在全局作用域声明的const变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。

通过制定const变量为extern,就可以在整个程序中访问const对象:

//file1.cpp//定义并初始化一个可以在其它文件里使用的const变量extern const int bufSize = fcn();//用函数返回值去初始化//file2.cppextern const int bufSize;//使用来自file1的const变量bufSizefor(int index = 0;index != bufSize;++index)    //....

5、引用(reference)就是对象的另一个名字。对引用的操作就是对该对象的操作,主要用作函数的形式参数。

不能定义引用类型的引用,但可以定义任何其他类型的引用。

引用必须用该类型的对象初始化:(其实不然,往下看)

int val = 1024;int &refVal = ival;//ok:a reference refers to ivalint &refVal2;//error:a reference must be initializedint &refVal3 = 10;error:initializer must be a object

引用只是它绑定的对象的另一名字,作用在引用上的所有操作事实上都是作用在该引用绑定的对象上

refVal += 2;//将refVal指向的对象ival加2int ii = refVal;//将和ival相关联的值赋给ii
当引用初始化后,只要该引用存在,她就保持绑定到初始化时指向的对象。不可能将引用绑定到另一个对象。

难点来了!

const引用,不是完全理解。总觉得差那么一丢丢

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

const int ival = 1024;const int &refVal = ival;//okint &ref2 = ival;//error
可以读取但不能修改refVal,因此,任何对refVal的赋值都是不合法的。这个限制有其意义:不能直接对ival赋值,因此不能通过使用refVal来修改ival.(这句尤其不懂,有看得懂的麻烦解释下,谢谢)

ref2是普通的非const引用,可以用来修改ref2指向的对象的值,与const对象的值不能改变矛盾,所以这样是错误的。

注意了,上面说的往下看就是让你看这里!

const引用可以初始化为不同类型的对象或者初始化为右值,下面几行代码来感受下

int &refVal3 = 10;//error:initializer must be an objectconst int &r = 42;//ok

再放个大招!

double dval = 3.1415926;const int &ri = dval;//编译器会转化成下面的代码int temp = dval;const int &ri = temp;
此时引用绑定到不同的类型上。如果ri不是const,那么可以给ri赋一个新的值,如98,但这样做只会修改temp,对double没啥影响啊。期望对ri的赋值能够修改dval的程序猿发现dval并没有变。

soooo,仅允许const引用绑定到需要临时使用的值完全避免了这个问题,因为const引用是只读的!

6、typedef:不说太多,举几个例子自己品味下

typedef double wages;typedef int exam_score;typedef wages salary;//间接同义语double//typedef名字用作类型说明符wages hourly,weekly;exam_score test_result;

7、枚举(enumeration)

还是要用一段篇幅说说我们为什么要用枚举。

我们经常需要为某些属性定义一组可选择的值。例如,文件打开的状态可能会有三种:输入、输出和追加。记录这些状态值的一种方法是使每种状态都与一个唯一的常数值相关联,我们可能会这么写代码

const int input = 0;const int output = 1;const int append = 2;
这种方法有个缺点,未指出这些值是相关联的。枚举提供了一种替代的方法,不但定义了整数常数集,而且还把它们聚集成组。

如何定义呢?包括关键字enum,后面是一个可选的枚举类型名,和一个用{}括起来、用逗号分开的枚举成员列表。

//input is 0,output is 1,append is 2enum open_modes{input,ouput,append};
默认第一个枚举成员赋值为0 ,以1递增。

可以为枚举成员提供初始值,这个初始值必须是一个常量表达式,编译器在编译时就能计算出结果的整型表达式。

enum Forms {shape = 1,sphere,cylinder,polygon};//shape is 1,sphere is 2,cylinder is 3,polygon is 4
shape显示初始化为1,其他的隐式初始化递增。

枚举成员值可以是不唯一的。

enum Points{ point2d = 2,point2w,             point3d = 3,point3w };
依次为2、3、3、4
枚举成员的值不可以改变,而且本身就是一个常量表达式,可用于任何需要常量表达式的地方
呼,有点累,不过还没说完。

每个enum类型都定义了一种新的类型。和其它类型一样可以去定义或者初始化enum类型。

枚举类型的对象的初始化或赋值,只能通过其枚举成员或同义枚举类型的其他对象来进行:

Points pt3d = point3d;//okPonits pt2d = 3;//error,即使3与一个Points枚举类型相关联也是错!pt2w = polygon;//errorpt2w = pt3d;//ok,这里的pt2w是已经定义


这一天看了很多重要的,很有收获!明天继续






0 0
原创粉丝点击