f(sixleaves) = sixleaves

重剑无锋 大巧不工

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  95 随笔 :: 0 文章 :: 7 评论 :: 0 Trackbacks

#

回顾第1章,第一章我们认识到了变量的定义,定义ing时赋值,操作符重载(Overloaded),和没有深入探讨的构造函数,成员函数的概念、符号直接量(与字符直接量的区别),还有输入输出缓冲模型之其好处(三个事件才会刷新缓冲区,输出到设备上,分别是,缓冲区已经满,遇到cin,显示要求刷新(如std::endl,控制符(manipulator)))。
这章我写得有点急切,应为之前C++学过,有些概念一跳而过,看不懂的,可以往下找红色字体处开始(从循环不变式分析处开始的分析,再回头来看这个)。
 1 #include <iostream>
 2 #include <string>
 3 
 4 int main() {
 5     //ask for the person's name
 6     std::cout << "Please enter your first name: ";
 7 
 8     //read the name
 9     std::string name;
10     std::cin >> name;
11 
12     //build the message that we intend to write
13     const std:;string greeting = "Hello, " + name + "!";
14 
15     //we have to rewrite this part
16 }
#
#分析:我们现在需要重写(重构//we have...后面的代码),应该这样思考,以前的那个程序不具备好的可扩展性,为什么呢?首先如果要求输入的框架编程10行(空白#行变成10行),后面的代码久要多加很多行,一行行的进行输出。这时我们可以用循环对代码进行重构。我们先分析,在greeting上下空白行只有一行,所以我们用pad
#表示空白行,而总的行数为2 * pad + 3(头尾加greeting那行)。这样我们就可以让程序输出任意多行。于是有如下代码
const int pad = 1;

const int rows = pad * 2 + 3;
#另外我们这个输出的框架是要让左右两边的空白数和上下两端的空白数相同,所以也只需要定义一个变量就够了。每一行输出的字符数就是greeting的长度加上pad * 2加上两个#两个星号。即如下代码const std::string::size_type cols = greeting.size() + pad * 2 + 2;

 1 
 2 #include <iostream>
 3 #include <string>
 4 using std::cin;        using std::endl;
 5 using std::cout;       using std::string;
 6 int main() {
 7     cout << "Please enter your first name: ";
 8 
 9     string name;
10     cin >> name;
11 
12     const string greeting = "Hello, " + name + "!";
13 
14     const int pad = 1;
15 
16     const int rows = pad * 2 + 3;
17     const string::size_type cols = greeting.size() + pad * 2 + 2;
18 
19     cout << endl;
20 
21     // invariant:we have written r rows so far
22     for(int r = 0; r != rows; ++r) {
23 
24         string::size_type c = 0;
25 
26         // invariant:we have written c characters so far in the current row
27         while(c != cols) {
28 
29             if(r == pad + 1 && c == pad + 1) {
30                 cout << greeting;
31                 c += greeting.size();
32             } else {
33 
34                 if(r == 0 || r == rows -1 || c == 0 || c == cols - 1)
35                     cout << "*";
36                 else
37                     cout << " ";
38                 ++c;
39             }
40         }
41 
42         cout << endl;
43 
44     }
45     return 0;
46 }
#第一个::说明string名字定义在名字空间std中,而第二个::则表示size_type来自string类。std::string定义了size_type,用来表示一个string中含有的字#符数目。如果需要一个局部变量来表示一个string长度,可以使用std::string::size_type类型定义一个变量。
#size_type是一个无符号的类型
#输出边界字符,如果r = 0,由循环不变式可以知道,现在一行也没有输出。所以当r = row - 1,已经输出了row - 1行,接下来输出的是最后一个部分,类似的,如果c = 0,输出的将是第一列的部分。
#输出边界符号:
#那么我们如何判断输出greeting这行呢,由循环不变式,我们可以 r = pad + 1 时,c = pad + 1时,开始输出greeting。

#第二章写得有点乱,上面代码看不懂的,请看下面分析
#首先我们要介绍一个概念,叫做循环不变式,循环不变式就是我们设置一个断言,让该断言在该循环中始终都成立,结束后也成立,这样这个断言其实就是这段程序的意思。看如
#下代码:
//invariant:we have written r rows so far

int r = 0;
//setting r to 0 makes the invariant true

while(r != rows) {
    //we can assume that the invariant is true here
    
//waiting a row of output makes the invariant false
    std::cout << std::endl;
    //incrementing r makes the invariant true again
    r++;
}
//we can conclude that the invariant is true here
#首先你应该想一想要确保不变式始终为true,只要确保在循环进入点为true,一次循环结束点为true,那么这个不变式久永远为true,understand?如果还不理解,先吧我说
 #的这句话理解了,在继续往下看,不然你不知道我在讲什么东西!
 #我们的不变式就是上述断言invariant:we have written r rows so far 
 #我们分析过,不变式的两个断点,一个设在开头,一个在结尾,所以开头时r = 0。此时程序一行也没输出,不变式为true,在结尾处r++后,仍为true,为什么呢?举个例子,r = 0,进来之后,将输出一行,所以此时r不应该在为0,而应该为1.
 #这是每一行输出的框架,转换成for循环就是上面相应的代码,而至于另外一个循环一样个道理。
#下面再介绍一个重要的概念,这个概念我之前还真没学好,看完后,恍然大悟,大测大悟阿!那就是循环时的计数问题。
 #在C中C++中我们写循环经常是重int i = 0,从0开始是不?就算是,你是不是经常这样写for(int i = 0; i <= number; i++);但是更好的写法应该是for(int i = 0; i  #!= number; i++);为什么呢?请听我慢慢道来.
 #首先我们知道在不对称区间[0, rows)计数的话,很明显就是rows个数,但是如果你使用的是对称区间,[num,rows]则有rows - num + 1个数,是不是很不明显,再则从0开  #始一目了然,别说你看不出来,我在举个例子(0,66],和[21,86]哪一个你能快速判断出有几个数。
 #有的人又说,这算什么阿,我从1开始贝[1,66],不就多算一个数么,习惯就好。我想说,你说的没粗,但我懒,用不对称区间跟块算出,更不会出错。在则,用不对称区间的好  #处是容易和invariant(循环不变式)相结合,例如,如果你从1开始计数,有的人想我们把不变式改成现在输出第r行,但是这样是不能作为一个不变式的,所谓不变式,就是
 #这个断言永远正确,但是当你结束循环时r = rows + 1,就变成了输出第rows + 1行,但这个不变式就变成错的鸟,understand。
 #再则我们选者!=而不是<=来作为比较操作符。这个差别很小,但是很不一样,前者,循环结束时(只要没有在循环里break),就能判断此时r = rows,但是如果是后者,我  #们这能证明至少输出了rows行,为啥?回忆下学过的math,<=,是什么意思?
 #还有一条好处,我就不罗嗦了,综上所属,你可以发现从0开始计数的好处!,想当一时,在写链表时,就是因为这个计数问题,自己也整了个证明方法,哈哈,每想到早就有更  #简单的方式了。
#本人才疏学浅,看不懂的,可以留言讨论之。
posted @ 2014-02-21 16:21 swp 阅读(254) | 评论 (0)编辑 收藏

回顾:在第0章中我总结了重要的知识点有:字符串直接量、表达式的结构,操作数和操作符的定义,还有表达式的副作用、和std::cout结合<<操作符返回std::ostream类型,等知识点。
代码如下
 1 // ask for person's name, and greet the person
 2 
 3 #include <iostream>
 4 #include <string>
 5 
 6 int main() {
 7     //ask for the person's name
 8     std::cout << "Please enter your first name: ";
 9 
10     //read the name
11     std::string name;
12     std::cin >> name;
13 
14     //write a greeting
15     std::cout << "Hello, " << name << "!" << std::endl;
16     return 0;
17 }
#name就是一个变量(它的类型是std::string),而变量是一个有名字的对象(变量一定是对象,但对象不一定为变量,因为对象可以没有名字,而且对象对应系统中的一块内存)。
#line 11:是一个definition,即是一个定义,定义了一个名叫做name的std::string类型的变量。而且出现在一个函数提中,所以是一个local variable,当程序执行放到},就会销毁name变量,并且释放name占用的内存,以让其他变量使用。
#line 12:>>从标准输入中读取一个字符串,并且保存它在name对象中。当通过标准库读取一个字符串时,他会忽略输入中的所有空白符,而吧其他字符读取到name中,直到它遇到其他空白符或者文件结束标志。因此std::cin >> name;的结果是从标准输入中读取一个单词。
#输入输出库会把它的输出保存在buffer的内部数据结构上,通过缓存可以优化输出操作。(因为许多操作系统在向输出设备写入字符时需要花大量的时间)
#有三个事件会促使系统刷新缓冲区。
 #第一,缓存区满了,自动刷新。
 #第二,标准库被要求读取标准输入流。(即std::cin是std::istream类型)。如line 12.
 #第三,显示的要求刷新缓冲。(std::endl结束了输出行,并且刷新缓冲区)
# 1 //ask for a person's name, and generate a framed greeting
 2 #include <iostream>
 3 #include <string>
 4 
 5 int main() {
 6     std::cout << "Please enter your first name: ";
 7     std::string name;
 8     std::cin >> name;
 9     //build the message that we intend to write
10     const std::string greeting = "Hello, " + name + "!";
11 
12     //build the second and fourth lines of the input
13     const std::string spaces(greeting.size(), ' ');
14     const std::string second = "* " + spaces + " *";
15 
16     //build the first and fifth lines of the output
17     const std::string first(second.size(), '*');
18 
19     //write it all
20     std::cout << std::endl;
21     std::cout << first <<std::endl;
22     std::cout << second << std::endl;
23     std::cout << "* " << greeting << " *" << std::endl;
24     std::cout << second << std::endl;
25     std::cout << first << std::endl;
26 
27     return 0;
28 }
#greeting的定义包含三个新的概念
  #第一个:在定义变量时候,可以给定它的值。
  #第二个:用+来连接字符串,但是这两个中必须至少有一个是string对象。(+也是左结合性的)
   #(+在这里是连接作用),引出overloaded(重载)概念,这个操作符被重载了,因为其对不同操作数有不同的含义。
  #第三个:const可以作为变量定义的一部分,这么做保证在变量生存期内,不改变它的值。
   #如果一个变量定义为const,必须在定义时初始化,否则后面就不能再初始化。
#const std::string spaces(greeting.size(), ' ');来介绍另三个概念
  #第一个:构造函数
  #第二个:成员函数(member function),其实可以吧greeting看成对象,向其发送size消息获取其长度。
  #第三个:字符直接量。(用'(单引号),而字符串直接量则是用“号).字符直接量的类型是内置于语言核心的char类型。
posted @ 2014-02-20 20:38 swp 阅读(189) | 评论 (0)编辑 收藏

@import url(http://www.cppblog.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css); 前言:
选择自己心中最新欢的事情去做,毕竟之前计算机基础课程基础也打得不错,深入的学习C++。我一直相信母亲说过的一句话:“不要认为你做不到,只要你想去做,就一定能做得到”。自我勉励之。同时开博是为了总结自己学习,希望能对有些人有所帮助。

由very simple的小程序包含的许多C++基础知识。
1 //a small C++ program
2 #include <iostream>
3 
4 int main()
5 {
6      std::cout << "Hello, world!" << std::endl;
7      return 0;
8 }
#程序的第一行为注释,//是行注释。
#std是一个命名空间(名字空间)namespace
#对于return语句,return 0表示程序正常退出。 
#对于return语句,如果是函数的定义要求返回某种特殊类型的值,那么这个函数中所有的return语句都必须返回相应类型的值。
#剖析“Hello world!程序的总结”如下
   #该程序中有两个贯穿C++的概念,表达式和生存空间(scope)
    #表达式
     #表达式的作用是请求系统进行计算。计算后会生成一个结果,同时也可能会有一些副作用。(所谓的副作用就是它会影响程序或者系统的状态)。行6也是个表达式
     ,它的副作用是在标准输出流输出“Hello, world!”并且当行结束。
     #表达式由操作符和操作数(operand)构成。如行6,两个<<符号都是操作符,而剩下的std::cout、“Hello world”、std::endl则是操作数。
      #操作数:每个操作数都是一种类型。(即都表示一种数据结构和它适合的操作,它决定了操作符的脾气(产生的结果),总而言之只要记住操作数决定了操作符的               脾气。)(int表示整数类型,std::ostream定义为流输出,std::cout的类型是std::ostream)
      #操作符:<<操作符有左结合性(left-assocoative)左结合性是一种贪心思想,当表达式出现两个以上的<<,左结合性总是贪心左边的操作数。
      #分析:第一个<<是以std::cout作为它的左操作数,而以"Hello, world"作为右操作数。
            第二个<<的左操作数是一个(生成std::cout结果)的表达式,其类型是std::ostream.右操作数是std::endl,是一个mainpulator(控制符)。当<<
            左操作符是std::ostream,而右操作数是mainpulator时,<<会根mainpulator的语义来控制流输出,并且返回流作为它的结果。如:当mainpulator是
            std::endl时,它结束当前输出行。
    #scope
     #namespace:命名空间机制,相当于java中的包机制。
     #scope,中文称为生存空间,::是生存空间操作符,::左边是生存空间的表示符std,而右边就是定义在命名空间中的名字。
#“Hello,world”称为字符串直接量,字符串直接量中,一些字符前面加上(\)后具有特殊的意义。
posted @ 2014-02-19 22:10 swp 阅读(253) | 评论 (0)编辑 收藏

仅列出标题
共10页: First 2 3 4 5 6 7 8 9 10