yc's profileyc1229PhotosBlogLists Tools Help

Blog


    August 15

    第三章 标准库类型

    3.1命名空间的using声明
        使用using可以不需要加前缀namespace_name::的情况下访问命名空间中的名字。using声明的形式如下:
        using namespace::name;
        1.每个名字都需要一个using声明
        2.使用标准库存类型的类定义
        如果在头文件中放置using声明,就相当于包含该头文件的每个程序中都放置了同一using声明,不论该程序是否需要using声明。
        通常,头文件中应该只定义确实必要的东西。需要养成这个好习惯。
        3.2标准库string类型
        使用前需包含
        #include <string>
        using std::string;
        3.2.1string对象的定义和初始化
        几种初始string对象的方式
        string s1; 默认构造函数,s1为空串
        string s2(s1); 将s2初始化为s1的副本
        string s3("value"); 将s3初始化为一个字符串字面值副本
        string s4(n,'c'); 将s4初始化为字符'c'的n个副本
        3.2.2string对象的读写
        可以用iostream和string标准库,使用标准输入输出操作符来读写string对象:
        int main()
        {
            string s;
            cin>>s;
            cout<<s<<endl;
            return 0;
        }
        读取并忽略开关所有空白字符(如空格,换行符,制表符)
        读取字符直至再次遇到空白字符。
        1.读入未知数目的string对象
        string的输入操作符也会返回所读的数据流,可以把输入操作作为判断条件:
        string word;
        while(cin>>word)
        2.用getline读取整行文本
        这个函数接受两个参数:一个输入流对象和一个string对象。getline不保存换行符,getline并不忽略行开头的换行符,只要getline遇到换行符,即使它是输入的第一个字符,getline也将停止读入并返回。
        string line;
        while(getline(cin,line))
        3.2.3string对象的操作
       
        常用的string操作
        s.empty() 如果s为空串,则返回true,否则返回false
        s.size() 返回s中字符的个数
        s[n] 返回s中位置为n的字符,位置从0开始计数
        s1+s2 把s1和s2连接成一个新字符串,返回新生成的字符串
        s1=s2 把s1内容替换成s2的副本
        v1==v2 比较v1与v2的内容,相等则返回true,否则返回false
        !=,<,<= 保持这些操作符惯有的含义
        >,>=
        2.string::size_type类型
        size操作返回的是string::size_type类型的值。
        string类类型和许多其他库类型都定义了一些配套类型。通过这些配套类型,库类型的使用就能与机器无关。
        任何存储string的size操作结果的变量必须为string::size_type类型。特别重要的是,不要把size的返回值赋给一个int变量。
        int类型最大值 32767
        3.string关系操作符
        如果两个string对象长度不同,且短的string对象与长的string对象的前面部分相匹配,则短的string对象小于长的string对象。
        如果两个string对象的字符不同,则比较第一个不匹配的字符。
        4.string对象的赋值
        size()不算\0 算\n
        大多数string库类型的赋值等操作的实现都会遇到一些效率上的问题,但值得注意的是,从概念上讲,赋值操作确实需要做一些工作。
        6.和字符串字面值的连接
        当进行string对象和字符串字面值混合连接操作时,+操作符的左右操作数必须至少有一个是string类型的。
        9.计算下标值
        string对象的索引变量最好也用string::size_type类型。
        3.2.4string对象中字符的处理
        cctype定义的函数
        isalnum(c) 如果c是字母或数字,则为true
        isalpha(c) 如果c是字母,则为true
        iscntrl(c) 如果c是控制字符,则为true
        isdigit(c) 如果c是数字,则为true
        isgraph(c) 如果c不是空格,但可打印,则为true
        islower(c) 如果c是小写字母,则为true
        isprint(c) 如果c是可打印的字符,则为true
        ispunct(c) 如果c是标点符号,则为true
        isspace(c) 如果c是空白字符,则为true
        isupper(c) 如果c是大写字母,则为true
        isxdigit(c) 如果c是十六进制数,则为true
        tolower(c) 如果c是大写字母,则返回其小写字母形式,否则直接返回c
        toupper(c) 如果c是小写字母,则返回其大写字母形式,否则直接返回c
        可打印的字符是指那些可以显式表示的字符,空白字符则空格、制表符、垂直制表符、回车符、换行符和进纸符中的任意一种;标点符号则是除了数字、字母或(可打印的)空白字符(如空格)以外的其他字符。
        cname头文件中定义的名字都定义在命名空间std内,而.h版本中的名字却不是这样。
        3.3标准库vector类型
        #include <vector>
        using std::vector;
        必须说明vector保存何种对象的类型,通过将类型放在类模板名称后面的尖括号中来指定类型:
        vector<int> ivec;
        3.3.1vector对象的定义和初始化
        几种初始化vector对象的方式
        vector<T> v1; vector保存类型为T的对象,默认构造函数v1为空
        vector<T> v2(v1); v2是v1的一个副本
        vector<T> v3(n,i); v3包含n个为i的元素
        vector<T> v4(n); v4含有值初始化的元素的n个副本
        1.创建确定个数的元素
        当把一个vector对象复制到另一个vector对象时,这两个对象必须保存同一种元素类型。
        虽然可以对给定元素个数的vector对象预先分配内存,但更有效的方法是先初始化一个空vector对象,然后再动态地增加元素。
        2.值初始化
        如果vector保存内置类型(如int类型)的元素,那么标准库将用0值创建元素初始式。
        如果vector保存的是含有构造函数的类类型(如string)的元素,标准库将用该类型的默认构造函数创建元素初始化式。
        3.3.2vector对象的操作
        vector操作
        v.empty() 如果v为空,则返回true,否则返回false
        v.size() 返回v中元素的个数
        v.push_back(t) 在v的末尾增加一个值为t的元素
        v[n] 返回v位置为n的元素
        v1=v2 把v1的元素替换为v2元素的副本
        v1==v2 如果v1与v2相等,则返回true
        !=,<,<=,>,>= 保持这些操作符惯有的含义
        1.vector对象的size
        使用size_type类型时,必须指出该类型是在哪定义的。vector类型总是包括vector的元素类型:
        vector<int>::size_type //ok
        vector::size_type //error
        3.vector的下标操作
        for (vector<int>::size_type ix=0;ix!=ivec.size();++ix)
            ivec[ix]=0;
        安全的泛型编程
        for循环的判断条件用!=而不用<来测试vector下标值是否越界。上例中没有在for循环之前就调用size成员函数并保存其返回的值,而是在for语句头中调用size成员函数。
        调用size成员函数而不保存它返回的值,在这个例子中同样不是必需的,但这反映了一种良好的编程习惯。在C++中,有些数据结构(如vector)可以动态增长。
        4.下标操作不添加元素
        所谓的“缓冲区溢出”错误就是对不存在的元素进行下标操作的结果。
        3.4迭代器简介
        所有标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作。
        1.容器的iterator类型
        每个标准库容器类型都定义了一个名为iterator的成员(迭代器)
        2.begin和end操作
        每种容器都定义了一对命名为begin和end的函数,用于返回迭代器。如果容器中有元素的话,由begin返回的迭代器指向第一个元素:
        vector<int>::iterator iter=ivec.begin();
        由end操作返回的迭代器指向vector的“末端元素的下一个”。通常称为超出末端迭代器。如果vector为空,begin返回的迭代器与end返回的迭代器相同。
        由end操作返回的迭代器并不指向vector中任何实际的元素。
        3.vector迭代器的自增和解引用运算
        迭代器类型可使用解引用操作符(*操作符)来访问迭代器所指向的元素:
        *iter=0;
        如果iter指向第一个元素,则++iter指向第二个元素。
        由于end操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作(可以进行自减操作,可以使指向end的迭代器指向最后一个元素)。
        4.迭代器的其他操作
        用==或!=操作符来比较两个迭代器,如果两个迭代器对象指向同一个元素,则它们相等,否则就不相等。
        6.const_iterator 可以改变指向的元素,不能改变指向元素的值。
        const vector<int>::iterator cit=num.begin() 可以改变元素值,不能改变指向的元素。
        当对const_iterator类型解引用时,返回的一个const值。不允许用const_iterator进行赋值。
        声明一个const迭代器时,必须初始化迭代器。
        const vector<int>::iterator cit=nums.begin()
        迭代器的算术操作
        iter+n
        iter-n
        加上或减去的值的类型应该是vector的size_type或difference_type类型(一种由vector类型定义的singed类型,用于存储任意两个迭代器间的距离)。
        iter1-iter2
        该表达式用来计算两个迭代器对象的距离,该距离是名为difference_type的signed类型的值。iter1与iter2两者必须都指向同一vector中的元素,或者指向vector末端之后的下一个元素。
        下面语句直接定位于vector的中间元素:
        vector<int>::iterator mid=vi.begin()+vi.size()/2;
        任何改变vector长度的操作都会使已存在的迭代器失效。例如,在调用push_back之后,就不能再依赖指向vector的迭代器的值了。
        3.5标准库bitset类型
        #include <bitset>
        using std::bitset;
        3.5.1bitset对象的定义和初始化
        在定义bitset时,要明确bitset含有多少位,须在尖括号内给出这的长度值。
        bitset<32> bitvec;
        初始化bitset对象的方法
        bitset<n> b; b有n位,每位都为0
        bitset<n> b(u); b是unsigned long型u的一个副本
        bitset<n> b(s); b是string对象s中含有的位串的副本
        bitset<n> b(s,pos,n); b是s中从位置pos开始的n个位的副本
        1.用unsigned值初始化bitset对象
        如果bitset类型长度大于unsigned long值的二进制位数,则其余的高阶位将置为0;如果bitset类型长度小于unsigned long值的二进制位数,则只使用unsigned值中的低阶位,超过bitset类型长度的高阶位将被丢弃。
        可以用0xfff初始化bitset对象。
        2.用string对象初始化bitset对象
        当用string对象初始化bitset对象时,string对象直接表示为位模式。从string对象读入位集的顺序是从右向左。
        string str("1111111000000011001101");
        bitset<32> bitvec5(str,5,4); //1100 从str[5]开始取
        bitset<32> bitvec6(str,str.size()-4); //use last 4 characters
        如果省略第三参数则意味着取从开始位置一直到string末尾的所有字符。
        3.5.2bitset对象上的操作
        bitset操作
        b.any() b中是否存在置为1的二进制位?
        b.none() b中不存在置为1的二进制位吗?
        b.count() b中置为1的二进制位的个数
        b.size() b中二进制位的个数
        b[pos] 访问b中在pos处的二进制位
        b.test(pos) b中在pos处的二进制位是否为1?
        b.set() 把b中所有二进制位都置为1
        b.set(pos) 把b中在pos处的二进制位置为1
        b.reset() 把b中所有二进制位都置为0
        b.reset(pos) 把b中在pos处的二进制位置为0
        b.filp() 把b中所有二进制位逐位取反
        b.filp(pos) 把b中在pos处的二进制位取反
        b.to_ulong() 用b中同样的二进制位返回一个unsigned long值
        os<<b 把b中的位集输出到os流
        1.测试整个bitset对象
        count操作的返回类型是标准中命名为size_t的类型。size_t类型定义在cstddef头文件中。它是一个与机器相关的unsigned类型,其大小足以保证存储内存中对象的大小。
        bitset和size操作返回值的类型是size_t。
        3.对整个bitset对象进行设置
        flip操作可以对bitset对象的所有位或个别位取反:
        bitvec.flip(0);
        bitvec[0].flip();
        bitvec.flip();
        4.获取bitset对象的值
        仅当bitset类型的长度小于或等于unsigned long的长度时,才可以使用to_ulong操作。
    August 07

    《美丽心灵》

    纳什均衡
     
     
     
     
      1950年和1951年纳什的两篇关于非合作博弈论的重要论文,彻底改变了人们对竞争和市场的看法。他证明了非合作博弈及其均衡解,并证明了均衡解的存在性,即著名的纳什均衡。从而揭示了博弈均衡与经济均衡的内在联系。纳什的研究奠定了现代非合作博弈论的基石,后来的博弈论研究基本上都沿着这条主线展开的。然而,纳什天才的发现却遭到冯·诺依曼的断然否定,在此之前他还受到爱因斯坦的冷遇。但是骨子里挑战权威、藐视权威的本性,使纳什坚持了自己的观点,终成一代大师。要不是30多年的严重精神病折磨,恐怕他早已
     
    站在诺贝尔奖的领奖台上了,而且也绝不会与其他人分享这一殊荣。

      纳什是一个非常天才的数学家,他的主要贡献是1950至1951年在普林斯顿读博士学位时做出的。然而,他的天才发现———非合作博弈的均衡,即“纳什均衡”并不是一帆风顺的。

      1948年纳什到普林斯顿大学读数学系的博士。那一年他还不到20岁。当时普林斯顿可谓人杰地灵,大师如云。爱因斯坦、冯·诺依曼、列夫谢茨(数学系主任)、阿尔伯特·塔克、阿伦佐·切奇、哈罗德·库恩、诺尔曼·斯蒂恩罗德、埃尔夫·福克斯……等全都在这里。博弈论主要是由冯·诺依曼(1903—1957)创所立的。他是一位出生于匈牙利的天才的数学家。他不仅创立了经济博弈论,而且发明了计算机。早在20世纪初,塞梅鲁(Zermelo)、鲍罗(Borel)和冯·诺伊曼已经开始研究博弈的准确的数学表达,直到1939年,冯·诺依曼遇到经济学家奥斯卡·摩根斯特恩(Oskar Morgenstern),并与其合作才使博弈论进入经济学的广阔领域。

      1944年他与奥斯卡·摩根斯特恩合著的巨作《博弈论与经济行为》出版,标志着现代系统博弈理论的的初步形成。尽管对具有博弈性质的问题的研究可以追溯到19世纪甚至更早。例如,1838年古诺(Cournot)简单双寡头垄断博弈;1883年伯特兰和1925年艾奇沃奇思研究了两个寡头的产量与价格垄断;2000多年前中国著名军事家孙武的后代孙膑利用博弈论方法帮助田忌赛马取胜等等都属于早期博弈论的萌芽,其特点是零星的,片断的研究,带有很大的偶然性,很不系统。冯·诺依曼和摩根斯特恩的《博弈论与经济行为》一书中提出的标准型、扩展型和合作型博弈模型解的概念和分析方法,奠定了这门学科的理论基础。合作型博弈在20世纪50年代达到了巅峰期。然而,诺依曼的博弈论的局限性也日益暴露出来,由于它过于抽象,使应用范围受到很大限制,在很长时间里,人们对博弈论的研究知之甚少,只是少数数学家的专利,所以,影响力很有限。正是在这个时候,非合作博弈———“纳什均衡”应运而生了,它标志着博弈论的新时代的开始!纳什不是一个按部就班的学生,他经常旷课。据他的同学们回忆,他们根本想不起来曾经什么时候和纳什一起完完整整地上过一门必修课,但纳什争辩说,至少上过斯蒂恩罗德的代数拓扑学。斯蒂恩罗德恰恰是这门学科的创立者,可是,没上几次课,纳什就认定这门课不符合他的口味。于是,又走人了。然而,纳什毕竟是一位英才天纵的非凡人物,他广泛涉猎数学王国的每一个分支,如拓扑学、代数几何学、逻辑学、博弈论等等,深深地为之着迷。纳什经常显示出他与众不同的自信和自负,充满咄咄逼人的学术野心。1950年整个夏天纳什都忙于应付紧张的考试,他的博弈论研究工作被迫中断,他感到这是莫大的浪费。殊不知这种暂时的“放弃”,使原来模糊、杂乱和无绪的若干念头,在潜意识的持续思考下,逐步形成一条清晰的脉络,突然来了灵感!这一年的10月,他骤感才思潮涌,梦笔生花。其中一个最耀眼的亮点就是日后被称之为“纳什均衡”的非合作博弈均衡的概念。纳什的主要学术贡献体现在1950年和1951年的两篇论文之中(包括一篇博士论文)。1950年他才把自己的研究成果写成题为“非合作博弈”的长篇博士论文,1950年11月刊登在美国全国科学院每月公报上,立即引起轰动。说起来这全靠师兄戴维·盖尔之功,就在遭到冯·诺依曼贬低几天之后,他遇到盖尔,告诉他自己已经将冯·诺依曼的“最小最大原理”(minimax solution)推到非合作博弈领域,找到了普遍化的方法和均衡点。盖尔听得很认真,他终于意识到纳什的思路比冯·诺伊曼的合作博弈的理论更能反映现实的情况,而对其严密优美的数学证明极为赞叹。盖尔建议他马上整理出来发表,以免被别人捷足先登。纳什这个初出茅庐的小子,根本不知道竞争的险恶,从未想过要这么做。结果还是盖尔充当了他的“经纪人”,代为起草致科学院的短信,系主任列夫谢茨则亲自将文稿递交给科学院。纳什写的文章不多,就那么几篇,但已经足够了,因为都是精品中的精品。这一点也是值得我们深思的。国内提一个教授,要求在“核心的刊物”上发表多少篇文章。按照这个标准可能纳什还不一定够资格。

      1996年诺贝尔经济学奖得主莫尔里斯当牛津大学艾奇沃思经济学讲座教授时也没有发表过什么文章,特殊的人才,必须有特殊的选拔办法。

      纳什在上大学时就开始从事纯数学的博弈论研究,1948年进入普林斯顿大学后更是如鱼得水。20岁出头已成为闻名世界的数学家。特别是在经济博弈论领域,他做出了划时代的贡献,是继冯·诺依曼之后最伟大的博弈论大师之一。他提出的著名的纳什均衡的概念在非合作博弈理论中起着核心的作用。后续的研究者对博弈论的贡献,都是建立在这一概念之上的。由于纳什均衡的提出和不断完善为博弈论广泛应用于经济学、管理学、社会学、政治学、军事科学等领域奠定了坚实的理论基础。

    July 19

    第二章 变量和基本类型

        2.1基本内置类型
        C++标准规定了每个算术类型的最小存储空间,但它并不阻止编译器使用更大的存储空间。对于int类型,几乎所有的编译器使用的存储空间都比所要求的大。
        这些类型所能表示的最大(最小)值也因机器的不同而有所不同。
        wchar_t 宽字符型 16位
        2.1.1整型
        通常将8位的块作为一个字节,32位或4个字符作为一个"字"。(64位机器中64位为一个字)   
        一般short类型为半个机器字长,int类型为一个机器字长,而long类型为一个或两个机器字长(在32位机器中int类型和long类型通常字长是相同的)。
        bool类型表示真值true和false。0值算术类型代表false,任何非0的值都代表true。
        1.带符号和无符号类型
        整型int、short和long都默认为带符号型,要获得无符号型则必须指定该类型为unsigned,unsigned int类型可以简写为unsigned。
        2.整型值的表示
        C++标准并未定义signed类型如何用位来表示,而是由每个编译器自由决定如何表示signed类型。这些表示方法会影响signed类型的取值范围。8位signed类型的取值肯定至少是从-127到127,但也有许多实现允许取值从-128到127。
        3.整型的赋值
        对于unsigned类型来说,编译器必须调整越界值使其满足要求。编译器会将该对unsigned类型的可能取值求模,然后取所得值。比如8位的unsigned char,其取值范围从0到255(包括255)。如果赋给超出这个范围的值,那么编译器将会取该值对256求模后的值。例如,如果试图将336存储到8位的unsigned char中,则实际赋值为80,因为80是336对256求模后的值。
        C++中,把负值赋给unsigned对象是完全合法的,其结果是该负数对该类型的取值个数求模后的值。所以,如果把-1赋给8位的unsigned char,那么结果是255,因为255是-1对256求模后的值。
        当将超过取值范围的值赋给signed类型时,由编译器决定实际赋的值,在实际操作中,很多的编译器处理signed类型的方式和unsigned类型类似。
        2.1.2浮点型
        对于实际的程序来说,float类型精度通常是不够的——float型只能保证6位有效数字,而double型至少可以保证10位有效数字,能满足大多数计算的需要。
        当执行整型算术运算时,很少使用short类型,比较典型的情况是值“截断”以至于因越界而变成很大的负数,虽然char类型被当作signed类型,在另一些应用中则被当作unsigned类型,因此把char类型作为计算类型使用时容易出问题。
        使用double类型基本上不会有错。在float类型中隐式的精度损失是不能忽视的,而double类型精度代价相对于float类型精度代价可以忽略。事实上,有些机器上,double类型比float类型的计算要快得多,long double类型提供的精度通常没有必要,而且还需要承担额外的运行代价。
        2.2字面值常量
        只有内置类型存在字面值,没有类类型的字面值。
        1.整型字面值规则
        20 //十进制
        024 //八进制
        0x14 //十六进制
        以0开头的字面值整数常量表示八进制,以0x或0X开头的表示十六进制。
        字面值整数常量的类型默认int或long类型。其精度类型决定于字面值——其值适合int就是int类型,比int大的值就是long类型。通过增加后缀,能够强制将字面值整数常量转换为long、unsigned或unsigned long类型。通过在数值后面加L或者l指定常量为long类型。
        定义长整型时,应该使用大写字母L。小写字母l很容易和数值l混淆。
        可通过在数值后面加U或u定义unsigned类型。
        128u //unsigned
        1024UL //unsigned long
        1L //long
        8Lu //unsigned long
        没有short类型的字面值常量。
        2.浮点字面值规则
        通常可以用十进制或者科学计数法来表示浮点字面值常量。使用科学计数法时,指数用E或者e表示。默认的浮点字面值常量为double类型。在数值的后面加上F或f表示单精度。同样加上L或者l表示扩展精度。
        3.布尔字面值和字符字面值
        单词true和false是布尔型的字面值
        在字符字面值上加L就能够得到wchar+t类型的宽字符字面值,如:
        L'a'
        4.非打印字符的转义序列
        我们可以将任何字符表示为以下形式的通用转义字符:
        \ooo
        这里\ooo表示三个八进制数字,这三个数字表示字符的数字值。
        同样也可以用十六进制转义字符来定义字符:
        \xddd
        5.字符串字面值
        为了兼容C语言,C++中所有的字符串字面值都由编译器自动在末尾加一个空字符(\0)
        存在宽字符串字面值,一样也在前面加“L”
        L"a wide string literal"
        宽字符串字面值一串常量字符,同样以一个宽空字符结束。
        6.字符串字面值的连接
        std::cout<<"a multi-line"
                   "b "
                   "c "
                 <<endl;
        std::cout<<"multi=line" L"literal"<<std::endl;
        其结果是未定义的,也就是说,连接不同类型的行为标准没有定义。
        7.多行字面值
        在一行的末尾加一反斜线符号可将此行和下一行当作同一行处理。(\)
        注意反斜线符号必须是该行的尾字符——不允许有注释或空格符。同样,后继行行首的任何空格和制表符都是字符串字面值的一部分。正因如此,长字符串字面值的后继行才不会有正常的缩进。
        通常程序不应该依赖机器相当的行为。
        2.3.1什么是变量
        (1)左值:左值可以出现在赋值语句的左边或右边
        (2)右值:右值只能出现在赋值的右边,不能出现在赋值语句的左边。
       
        2.3.2变量名
        语言本身并没有限制变量名的长度,但考虑到将会阅读和/或修改我们的代码的其他人,变量名不应太长。
        1.C++关键字
        标识符不能包含两个连续的下划线,也不能以下划线开头后面紧跟一个大写字母。有些标识符(在函数外定义的标识符)不能以下划线开头。
        2.3.3定义对象
        1.初始化
        定义时指定了初始值的对象被称为已初始化的。C++支持两种初始化变量的形式:复制初始化和直接初始化。复制初始化语法用等号,直接初始化则是把初始化式放在括号中:
        int ival(1024); //直接初始化
        int ival=1024; //复制初始化
        初始化指创建变量并给它赋初始值,而赋值则是擦除对象当前值并用新值代替。
        2.使用多个初始化式
        对人置类型来说,复制初始化和直接初始化几乎没有差别。
        使用string时必须包含string头文件。string定义在std命名空间中。
        2.3.4变量初始化规则
        当定义没有初始化式的变量时,系统有时候会帮我们初始化变量。这时,系统提供什么样的值取决于变量的类型,也取决于变量定义的位置。
        1.内置类型变量的初始化
        在函数体外定义的变量都初始化成0。
        建议每个内置类型的对象都要初始化,虽然这样并不总是必需的,但是会更加容易和安全,除非你确实忽略初始化式不会带来风险。
        2.3.5声明和定义
        变量的定义用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义。
        声明用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字。可以通过使用extern关键字声明变量名而不定义它。
        extern int i;
        extern声明不是定义,由不分配存储空间。事实上,它只是说明变量定义在程序的其他地方。程序中变量可以声明多次,但只有定义一次。
        只有当声明也是定义时,声明才可以有初始化式,因为只有定义才分配存储空间,如果声明有初始化式,那么它可以被当作是定义,即使声明标记为extern:
        extern double pi=3.1416;
        虽然使用了extern,但是这条语句还是定义了pi,分配初始化了存储空间,只有当extern声明位于函数外部时,才可以含有初始化式。
        2.4 const限定符
        魔数:它的意义在上下文中没有体现出来,好像这个数是魔术般地从空中出现的。
        1.定义const对象
        因为常量在定义后就不能被修改,所以定义时必须初始化。
        2.const对象默认为文件的局部变量
        除非特别说明,在全局作用声明的const变量是定义该对象的文件的局部变量,此变量只存在于那个文件中,不能被其他文件访问。
        通过const变量为extern,就可以在整个程序中访问const对象:
        //file_1.cc
        extern const int bufSize=fcn();
        //file_2.cc
        extern const int bufSize;
        非const变量默认为extern。要使const变量能够在其他的文件中访问,必须显式地指定它为extern。
        2.5引用
        3.const引用
        const int ival=1024
        const int &refVal=ival; //ok
        int &ref2=ival; //error
        const引用可以初始化不同类型的对象或者初始化为右值,如字面值常量:
        int i=42;
        const int &r=42;
        const int &r2=r+i;
        非const引用只能绑定到与该引用同类的对象。const引用则可以绑定到不同但相关的类型的对象或绑定到右值。
        2.7枚举
        1.定义和初始化枚举
        默认地,第一个枚举成员赋值为0,后面的每个枚举成员赋的值比前面的大1。
        2.枚举成员是常量
        enum Forms {shape=1,sphere,cylinder,polygon}; //shape is 1
        //sphere is 2,cylinder is 3,polygon is 4
        enum Points {point2d=2,point2w,point3d=3,point3w};
        //point2d is 2,point2w is 3,point3d is 3,point3w is 4
        3.每个enum都定义一种唯一的类型
        每个enum都定义了一种新的类型。枚举类型的对象的初始化或赋值,只能通过期枚举成员或同一枚举类型的其他对象来进行:
        Points pt3d=point3d; //ok
        Points pt2w=3; //error
        pt2w=polygon; //error
        pt2w=pt3d; //ok
        2.8类类型
        1.从操作开始设计类
        每个类都定义了一个接口和一个实现。接口由使用该类的代码需要执行的操作组成。实现一般包括该类所需要的数据。
        定义类时,通常先定义该类的接口,通过这些操作,可以决定该类完成其功能所需要的数据,以及是否需要定义一些函数来支持该类的实现。
        2.定义Sales_item类
        操作和数据是类的一部分,也称为类的成员。操作称为成员函数,而数据则称为数据成员。
        3.类的数据成员
        一般不能把类成员的初始化作为其定义的一部分。当定义数据成员时,只能指定该数据成员的名字和类型。
        4.访问标号
        访问标号public、private可以多次出现在类定义中。给定的访问标号应用到下一个访问标号出现时为止。
        5.使用struct关键字
        如果使用class关键字来定义类,那么定义在第一个访问标号前的任何成员都隐式指定为private;如果使用struct关键字,那么这些成员都是public。使用class还是struct关键字来定义类,仅仅影响默认的初始访问级别。
        可以等效地定义Sales_item类为:
        struct Sales_item{
        private:
         std:string isbn;
         unsigned units_sold;
         double revenue;
        };
        用class和struct关键字定义类的唯一差别在于默认访问级别:默认情况下,struct的成员为public,而class的成员为private。
        2.9编写自己的头文件
        2.9.1设计自己的头文件
        1.头文件用于声明而不是用于定义
        下列语句是一些定义,所以不应该放在头文件里:
        extern int ival=10;
        double fica_rate;
        虽然ival声明为extern,但是它有初始化式,代表这条语句是一个定义。类似地,fica_rate的声明虽然没有初始化,但也是一个定义,因为没有关键extern
        因为头文件包含在多个源文件中,所以不应该含有变量或函数的定义。
        对于头文件不应该含有定义这一规则,有三个例外。头文件可以定义类,值在编译时就已知道的const对象和inline函数
       
        2.9.2预处理器的简单介始
        1.头文件经常需要其他头文件
        包含其他头文件是如此司空见惯,甚至一个头文件被多次包含进一个源文件也不稀奇。例如,使用Sales_item头文件的程序也可能使用string库。该程序不会(也不应该)知道Sales_item头文件使用了string库。在这种情况下,string头文件被包含了两次:一次是通过程序本身直接包含,别一次是通过包含Sales_item头文件而间接包含。
        我们必须保证多次包含同一头文件不会引起该头文件定义的类和对象被多次定义。使得头文件安全的通用做法,是使用预处理器定义头文件保护符。头文件保护符用于避免在已经见到头文件的情况下重新处理该头文件的内容。
        2.避免多重包含
        #ifndef指示检测指定的预处理器变量是否未定义。如果预处理器变量未定义,那么跟在其后面的所有指示都被处理,直到出现#endif。
        头文件应该含有保护符,即使这些头文件还会被其他头文件包含。编写头文件保护符并不困难,而且如果头文件被包含多次,它可以避免难以理解的编译错误。

    第一章 快速入门

        通常main函数返回的值来确定程序是否成功执行完毕,大多数系统中,main函数返回值为0表示main函数成功执行完毕,任何其他非0的返回值都有操作系统定义的含义。main函数的形参个数有限。main函数的返回值必须是int型。
        函数返回值必须和函数的返回值相同,或者可以转换成函数类型。
        c:\aa>cl -GX prog1.cpp  微软编译器采用该命令进行编译 prog1.cpp是文件名
        在windows系统下查看状态,键入 c:\aa>echo %ERRORLEVEL%
        术语“流”试图说明字符是随着时间顺序生成或消耗的。
        当操作是输出操作符时,结果是左操作数的值,输出操作返回的值是输出流本身。
        endl 具有输出换行的效果,并刷新与设备相关联的缓冲区。通过刷新缓冲区,用户可立即看到写入到流中的输出。
        std::cout std::endl 前缀std::表明cout和endl是定义在命名空间std中的。使用命名空间程序员可以避免与库中定义的名字相同而起来无意冲突。因为标准库定义的名字是定义在命名空间中,所以我们可以按自己的意图使用相同的名字。
        "The sum of" 称为字符串字面值。
        已初始化的变量是指变量在定义时就给定一个值,未初始化变量则未给定初始值。给变量一个初始值几乎总是正确的,但不要求必须这样做。
        单行注释 //
        多行注释 /*    注释对不可嵌套
                      */
        退出for循环后,变量val不再可访问,循环终止后使用val是不可能的,然而不是所有的编译器都有这一要求。(VC++里并不是这个规则)
        读入未知数目的输入 while (std::cin>>value) ;
        文件结束符 Ctrl+z
        一般来说,我们将类定义放入一个文件中,要使用该类的任何程序都必须包含这个文件。依据惯例,类类型存储在一个文件中,其文件名如同程序的源文件名一样。
        标准库的头文件用尖括号<>括起来,非标准库的头文件用双引号" "括起来。