C++ Primer chapter 1

2018-06-17 21:50:07来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

第一章  开始

  本章介绍C++的打本份基础内容:类型、变量、表达式、语句以及函数。在这个过程中,文明会叫要介绍如何编译以及运行程序。

  在学习完本章并认真完成练习之后,将具备编写、编译机运行简单程序的能力。后续章节假定你已经掌握本章中介绍的语言特性,并将更详细地解释这些特性。

  问题:书店保存所有销售记录的档案,每条记录保存了某本书的一次销售信息(一册或是多册)。

      每条销售记录包含三个数据项: {

      0-201-0353-x  // 书的ISBN号(国际标准书号,一本书的唯一标识)

      4        // 售出的册数

      24.99      // 书的单价

      };

      有时候,书店老板需要查询次档案,计算每本书的销售量销售额以及平均售价。 

      对于一本书来讲,

        销售量 = ∑每条销售记录.售出的册数;

        销售额 = ∑(每条销售记录.售出的册数 * 每条销售记录.书的单价)

        平均售价 = 销售额 / 销售量

   ------------------------------------------------------

    •   定义变量
    •   进行输入和输出
    •   使用数据结构保存数据
    •   检测两条记录是否有相同的ISBN
    •   包含一个循环来处理销售档案中的每条记录

 

 

  1.1  编写一个简单的C++程序

    • 每个C++程序都包含一个或多个函数(function),其中一个必须命名为main。OS通过调用main来运行C++程序。
    • 一个函数的定义包含四部分:返回类型(return type)、函数名(function name)、一个括号包围的形参列表(parameter list,允许为空)、以及函数体(function body)  
      • 虽然main函数在某种程度上比较特殊,但其定义与其他函数是一样的。
    • 重要概念:类型
      • 类型是程序设计最基本的概念之一,在本书中我们会反复遇到它。一种类型不仅定了数据元素的内容,还订了这类数据上可以进行的运算。
      • 程序所处理的数据都保存在变量中,而每个变量都有自己的类型。如果一个名为v的变量的类型为T,我们通常说“v具有类型T”,或等价的,“v是一个T类型的变量”

    1.1.1 

      • 源程序文件命名约定:后缀告诉系统这个文件是一个C++程序。  不同编译器使用不同的后缀命名约定,最常见C++程序文件后缀名包括.cc、.cxx、.cpp、.cp以及.C。
      • 从命令行运行编译器:通常是在一个控制台窗口内编译程序。
      • 访问main函数的返回值的方法依赖于系统。在UNIX和Windows系统中,执行完一个程序后,都可以通过echo命令获得其返回值:
        • UNIX系统    $ echo $?
        • Windows系统   $ echo %ERRORLEVEL%  (实际检验返回0)
      • 在一些系统中,及时文件就再当前目录或文件夹中,你也必须显示指出文件的位置。(例如:./a.out 或者 .\prog等)
1 int main (void) {
2     return 0;
3 }
View Code
      • $ echo $?  // 127
      • 当修改 return -1;  // 返回值-1通常被当作程序出现错误的标识。
      • 之后,在使用上述语句,系统如何处理main返回的错误标识:返回 255
      • 在使用GNU编译器的版本,你可能需要指定 -std=c++0x 参数来打开对C++11的支持

  1.2  初始输入输出

    • C++语言未定义任何输入输出(IO)语句,取而代之,包含了一个全面的标准库(standard library)来提供IO机制(以及很多其他设施)。
      • 一般来说类似 iostream, ios, istream, ostream 等等头文件,都在 /usr/include/c++/4.x/
    • iostream库包含两个基础类型 istream 和 ostream,分别表示输入流和输出流。
    • 一个流就是一个字符序列,是从IO设备读出 或 写入IO设备的。
      • 术语“流”(stream)想要表达的是:随着时间的推移,字符是顺序 生成 或 消耗 的。
    • 标准输入输出对象
      • 标准库定义了4个 IO 对象:
        • 处理输入,使用 cin 的 istream 类型的对象,此对象也被成为标准输入(standard input)
        • 处理输出,使用 cout 的 ostream 类型的对象,此对象也被成为标准输出(standard output)
        • 其它两个 ostream 对象:
          • cerr 通常用来输出警告和错误消息,因此也被称为标准错误(standard error)
          • clog 用来输出程序运行时的一般性信息。
      • 系统通常将程序所运行的窗口与这些对象关联起来。
        • 因此,当读取 cin ,数据将从程序正在运行的窗口读入,
        • 当向 cout、cerr 和 clog 写入数据时,将会写到同一个窗口。
    • 一个使用IO库的程序
      • 对于书店程序,需要将多条记录合并成单一的汇总记录(对于多条 ISBN 号相同的书的销售记录)。
      • 两个数相加,并使用IO库,使之能提示用户输入两个数,然后输出他们的和:
 1 #include <iostream>
 2 int main (void) {
 3     std::cout << "Enter two numbers: " << std::endl;
 4     // << 运算符接受两个运算对象:左侧的运算对象必须是一个ostream类型对象,右侧的运算对象是要打印的值。
 5     // 此运算符将给定的值写到给定的ostream对象中。
 6     // 输出运算符的计算结果就是其左侧运算对象,即计算结果即使我们写入给定值的那个ostream对象。
 7     //
 8     // << 运算符的返回值是左操作数的引用    ostream& operator<< (ostream& os, const string& str);
 9     int v1 = 0, v2 = 0;
10     std::cin >> v1 >> v2;    // 系统会将程序所运行的窗口和输入/输出流对象关联起来
11     std::cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << std::endl;
12     return 0;
13 }
View Code
    • 每个使用标准库设施的程序都必须包含相关的头文件。
      • #include 指令和头文件的名字必须在同一行中。通常情况下此类预处理指令必须出现在所有函数定义之外。
    • 向流写入数据
      • 表达式 std::cout << "Enter two numbers: " << std::endl; 使用输出运算符 (<<) 在标准输出上打印消息:
        • 第一个输出运算符给用户打印一条消息:是一个字符串字面值常量(string literal)是用一对双引号包围的字符序列。
          • 在双引号之间的文本将被打印到标准输出 (cout,并关联到当前运行程序的输出窗口)
        • 第二个输出运算符打印 endl,这是一个被称为操纵符(manipulator)的特殊值。
          • 写入endl 的效果是结束当前行,并将与设备关联的缓冲区(buffer)中的内容到设备中,
          • 缓冲刷新操作可以保证到目前为止程序所产生的所有输出都真正写入输出流中,而不是仅仅停留在内存中等待写入流。
        • 标准库定了不同版本的输入输出运算符,来处理不同类型的运算对象。
      • Warning:程序员常常在调试时添加打印语句。这类语句应该保证“一直”刷新流。否则,如果程序崩溃,输出可能还留在缓冲区中,从而导致关于程序崩溃位置的错误推断
    • 使用标准库中的名字
      • std::out, std::endl -- 前缀std::指出名字 cout 和 endl 是定义在名为 std 的名字空间(namespace)中的。
      • 名字空间可以帮助避免不经意的名字定义冲突,以及使用和库中相同名字导致的冲突。标准库定义的所有名字都在 std 名字空间中。
    • 从流中读取数据
      • 输入运算符 (>>)接受一个istream类型对象作为其左操作数,接受一个对象作为其右操作数。从给定的istream中读入数据,并存入给定对象中。
      • >> 返回其左操作数的引用,

  1.3  注释简介

    • 错误的注释比完全没有注释更糟糕,所以当修改代码时,不要忘记更新注释!
    • 注释界定符不能嵌套 使用
      • // /* 
      • // * 单行注释中的任何内容都会被编译器忽略
      • // * 包括嵌套的注释对儿也一样会被编译器忽略
      • // */

  1.4  控制流

    • 语句一般是顺序执行的:语句块的第一条语句首先执行,然后是第二条语句,一次类推。
    • 程序设计语言提供了许多不同的控制流语句,允许写出更为复杂的执行路径。

    1.4.1  while 语句

    • while 语句反复执行一段代码,直至给定的条件为假(0 / false / NULL)时为止。
    • while (condition
      • statement
    • while 语句的执行过程是交替地检测 condition 条件和执行关联的语句 statement, 直至condition 为假时停止。

    1.4.2  for 语句

    • 每个 for 语句都包含两个部分: 循环头和循环体。
    • 循环头控制循环体的执行次数,它由三部分组成:一个初始化语句(init-statement)、一个循环条件(condition)以及一个表达式(expression
    • for (init-statement(1); condition(2); self-increasement(4))
      • statemen(3)
      • for 循环的执行顺序是(1)(2)(3)(4)
      • for 循环常犯错误!!!--- 线性链表析构过程
/*    这种析构方式会导致Segmentation fault (core dumped)
            for (Node* next; m_head; next = m_head->m_next) {
                delete m_head;    // 当m_head != NULL时,delete m_head,对一个已经被delete的指针做间接成员访问是危险的!
                m_head = next;    // 因为在这里也没有为m_head赋有效的值;for循环的执行顺序问题:理解不够透彻/使用不够熟练
            }*/
View Code

 正确写法如下:

for (Node* next; m_head; m_head = next) {
                next = m_head->m_next;    // 一定要在循环体中将m_head的下一个节点的地址取出来,保存好;
                delete m_head;
            }
View Code
      • 使用前缀自增减的好处是:(对于基本类型没有) 对于类类型的对象X,在使用 X.operator++() 避免了值的拷贝
    • // 这里for循环中建议使用++c,因为前++的效率高:
      // 前++返回的只是操作数的引用、别名
      // 后++返回的是副本,如果此时时类对象的话,意味着要做一次拷贝构造!!!
      // 是以值的形式放回的,在调用处维护一个匿名对象(它是++之前的值),在for循环logic中根本没有用
      // for循环只需要自增就ok,前++只是自增,返回的只是引用,其实现使用指针实现的,不用做拷贝构造,引用效率高

1         X& operator++ (void) {
2             ++m_r;
3             ++m_i;
4             return *this;
5         }
View Code
    • 编写程序,从cin 读取一组数据,输出其和;(预先不知道要对多少个数据求和,就需要不断读取数据直到没有新的输入为止:)
 1 #include <iostream>
 2 using namespace std;
 3 int main (void) {
 4     int sum = 0, value = 0;
 5     // 读取数据直到遇到文件尾,计算所有读入的值的和
 6     while (cin >> value)
 7         sum += value;
 8     cout << "Sum is: " << sum << endl;
 9     return 0;
10 }
View Code
      • 数据读取操作是在while 的循环条件中完成的:while (std::cin >> value) --- while 循环条件的求值就是执行表达式 std::cin >> value
      • 此表达式从标准输入读取下一个数,保存在 value 中。输入运算符返回其左侧运算对象,因此循环条件实际上检测的是 std::cin 
      • 当使用一个 istream 对象作为条件时,其效果是检测流的状态
        • 如果流是有效的,即流未遇到错误,那么检测成功。
        • 当遇到文件尾(文件结束符 end-of-file EOF),或者遇到一个无效输入时(例如输入的值不是一个整数),istream对象的状态会变为无效。
      • 处于无效状态的 istream 对象会使条件变为假。
      • 因此while 循环会一直执行到遇到文件结束符(或输入错误)。
      • UNIX系统能够中,文件结束符输入时用 Ctrl + D。

    1.4.4  if 语句

    • if 语句支持’条件执行‘。
    • 编写程序:统计在输入中每个值连续出现了多少次:
 1 #include <iostream>
 2 using namespace std;
 3 int main (void) {
 4     int currVal = 0;                // currVal 是我们正在统计的数;
 5     int val = 0;                    // 我们将读入的新值存入val
 6     if (cin >> currVal) {
 7         int cnt = 1;                // 保存我们正在处理的当前值得个数
 8         while (cin >> val) {        // 读取剩余的数
 9             if (val == currVal)        // 如果值相同
10                 ++cnt;
11             else {                    // 否则,打印前一个值的个数
12                 cout << currVal << " occurs " << cnt << " times" << endl;
13                 currVal = val;        // 记住新值
14                 cnt = 1;            // 重置计数器
15             }
16         } 
17         // 记住打印文件中最后一个值的个数
18         cout << currVal << " occurs " << cnt << " times" << endl;
19     }
20     return 0;
21 }
View Code

 

  1.5  类简介

    • 如何定义一个数据结构(data structure)来表示销售数据。通过定义类 class 类定义自己定义的数据结构。
    • 一个类定义了一个类型,以及与其相关联的一组操作。
    • C++最初的一个设计焦点就是:能定义一个在使用方法、方式上像内置类型一样自然的类类型。
      • 类名
      • 他是在哪里定义的
      • 它支持什么操作

为了使用标准库的设施,必须包含相关的头文件;类似的,如果程序需要使用自定义的类,也要使用#include 命令来(访问)将其包含进来;

    • 习惯上,头文件根据其中定义的类的名字来命名。例如:书的每条销售记录,Sales_item 类定义在 Sales_item.h 头文件中;

    1.5.1  Sales_item 类

    • Sales_items 类的作用是表示一本书的:总销售额、售出册数、平均售价。
    • 为了使用一个类,不必关心它是如何实现的,只需要知道此类对象都可以执行什么操作。
      • 可以定义一个 Sales_item 类型的对象item  Sales_item item;
      • 调用一个名为isbn的函数,从一个 Sales_item 对象中提取ISBN书号
      • 用输入运算符(>>)和输出运算符(<<)读、写Sales_item类型的对象。
        • cin >> item;  // istream& operator>> (istream& is, Sales_item& item);
          • 输入一个item:实际需要用户输入哪些信息?
          • 录入每一本书的销售记录,用户需要单独输入其对应的:{isbn号、(该书的本次)销售数量、销售单价}
        • cout << item;  // ostream& operator<< (ostream& os, const Sales_item& item);
          • 对于操作:cout << 目的是输出一个 Sales_item 类型的对象
          • 老板/用户想看到的是某一本书销售记录?(何为销售记录?应该包含哪些醒目的信息?){isbn、销售册数、销售收益、平均售价。。。根据用户需求}
      • 用赋值运算符(=)将一个Sales_item对象的值赋予另一个Sales_item对象。
      • 用加法运算符(+)将两个Sales_item对象相加。两个对象必须表示同一本数(相同的ISBN)。加法的结果是一个新的Sales_item对象,其ISBN与两个运算对象相同,而其总销售额和售出册数则是两个运算对象的对应值之和。
      • 使用符合赋值运算符(+=)将一个Sales_item 对象加到另一个对象上。
    • 关键概念: 类定义了行为
      • 类Sales_item 的作者定义了类对象可以执行的所有动作。即,Sales_item 类定义了创建一个Sales_item 对象是会发上什么事情,以及对 Sales_item 对象进行赋值、加法或者输入输出运算时会发生什么事情。
      • 一般而言,类的作者决定了类类型对象上可以使用的所有操作。     
 1 #ifndef SALES_ITEM_H
 2 #define SALES_ITEM_H
 3 
 4 #include <iostream>
 5 #include <string>
 6 using namespace std;
 7 
 8 class Sales_item {            // 定义一个用来存放:一本书的‘销售记录’的类;都包含哪些必要信息?
 9     private:
10         string m_isbn;        // 国际标准书号ISBN,    卖了哪本书?
11         size_t m_nbook;        // 一次销售中卖出的数目    卖了多少本?
12         double m_revenue;    // 一次销售中获得的收益    赚了多少钱?
13     public:
14         Sales_item () : m_nbook (0), m_revenue (0.0) {}
15         Sales_item (const string& isbn) : m_isbn (isbn), m_nbook (0), m_revenue (0.0) {}
16         Sales_item (const string& isbn, size_t nbook, double revenue) 
17             : m_isbn (isbn), m_nbook (nbook), m_revenue (revenue) {}
18         Sales_item (istream& is) {    // 通过一个字符串或者输入流构造一个对象
19             is >> *this;            // istream& operator>> (istream&, Sales_item&);
20         }
21         /* 系统会提供缺省的拷贝构造函数形式如下:在没有浅拷贝风险的时候,缺省的够用;
22         Sales_item (const Sales_item& that) : m_isbn (that.m_isbn), m_nbook (that.m_nbook), m_revenue (that.m_revenue) {}
23         */
24         const Sales_item operator+ (const Sales_item& r) const {
25             // 什么是两次销售记录的相加 + 运算:当isbn相同时,对应的销售册数、销售收益各自相加
26             return Sales_item (m_isbn, this->m_nbook + r.m_nbook, this->m_revenue + r.m_revenue);
27         }
28         Sales_item& operator+= (const Sales_item& l) {
29             this->m_nbook += l.m_nbook;
30             m_revenue += l.m_revenue;
31             return *this;
32         }
33 //        bool same_isbn (const Sales_item& r) {    // 错误2
34         bool same_isbn (const Sales_item& r) const {
35             return this->m_isbn == r.m_isbn;
36         }
37         bool operator== (const Sales_item& r) const {
38             // 判断两个销售记录是否相同:
39             // 1.对于不同的isbn号,显然不是相同的销售记录
40             // 2.对于相同isbn号的同一本书的,有可能有多次销售记录:只有当两次记录的销售数量、销售收益也相等时
41 //            return same_isbn (r) &&                错误1
42 //            return const_cast<Sales_item&>(*this).same_isbn (r) &&    // 也是OK的,但是不太好
43             return (*this).same_isbn (r) && 
44                     m_nbook == r.m_nbook &&
45                     m_revenue == r.m_revenue;
46         }
47         bool operator!= (const Sales_item& r) {
48             return (*this == r);
49         }
50         double avg_price () const {
51             if (m_nbook) 
52                 return m_revenue / m_nbook;
53             else
54                 return 0;
55         }
56         friend istream& operator>> (istream& is, Sales_item& item) {
57             double price;            // 定义每本书的销售单价
58             is >> item.m_isbn >> item.m_nbook >> price;
59             if (is) 
60                 item.m_revenue = item.m_nbook * price;    // 此次销售记录对于isbn书的销售额
61             else
62                 item = Sales_item ();    // 如果is输入失败,重置Sales_item对象到默认状态
63             return is;
64         }
65         friend ostream& operator<< (ostream& os, const Sales_item& item) {
66             os << item.m_isbn << "\t" << item.m_nbook << "\t" << item.m_revenue << "\t" << item.avg_price ();
67             return os;
68         }
69 };
70 
71 #endif // SALES_ITEM_H
View Code
    • 读写 Sales_item
 1 #include <iostream>
 2 #include "Sales_item.h"
 3 using namespace std;
 4 int main (void) {
 5     Sales_item item;
 6     cout << "Please enter one sales item: " << endl;
 7     cin >> item;            // read isbn, number of copies sold, sales price
 8     cout << "isbn" << "\t\t" << "nbook" << "\t" << "revenue" << "\t" << "avg_price" << endl;
 9     cout << item << endl;    // isbn, number of sold, revenue, avg_price
10     return 0;
11 }
View Code
    • Sales_item 对象的加法
       1 #include <iostream>
       2 #include "Sales_item.h"
       3 using namespace std;
       4 int main (void) {
       5     Sales_item item1, item2;
       6     cin >> item1 >> item2;
       7     // first have to check item1 & item2 reponse the same isbn
       8 //    if (item1.same_isbn(item2)) {
       9     if (item1.isbn() == item2.isbn()) {
      10         cout << item1 + item2 << endl;
      11         return 0;
      12     }
      13     else {
      14         cerr << "Data must refer to same ISBN" << endl;
      15         return -1;
      16     }
      17 //    return 0;
      18 }
      View Code
    • 使用文件重定向
      • 当测试程序时,反复从键盘敲入这些销售记录作为程序的输入,是非常乏味的。大多数OS支持文件重定向,这种机制允许我们将标准输入和标准输出与命名文件关联起来:
        • $ item <infile > outfile
      • 假定$ 是OS提示符,add 操作已经编译为 item,则上述命令会从一个名为 infile 的文件读取销售记录,并将结果写入到一个名为 outfile 的文件中,两个文件都位于当前目录中。

     1.5.2  初识成员函数

    • 成员函数是定义为类的一部分的函数,有时也被称为方法(method)。通常定义一个类对象来调用成员函数。
      • item1.isbn () == item2.isbn ();  // 使用点运算符(.)来表达我们需要 “名为item1的对象的isbn成员” 
      • 点运算符(.)只能用于类类型对象:其左侧运算对象必须是一个类类型的对象,右侧运算对象必须是该类型的一个成员名,结果为右侧对象指定的成员。

 

  1.6  书店程序 

    • 现在已经贮备好完成书店程序了:
      • 从一个文件中读取销售记录<一次销售中:书ISBN号、销售册数、销售单价>;
      • 生成每本书的销售报告:显示<售出的册数、总销售额、平均售价>。
 1 #include <iostream>
 2 #include "Sales_item.h"
 3 using namespace std;
 4 int main (void) {
 5     Sales_item curr_item, item;
 6     if (cin >> curr_item) {
 7         int cnt = 1;
 8         while (cin >> item) {
 9 //            if (curr_item == item) 
10 //                ++cnt;
11 //            if (curr_item.same_isbn (item)) {
12             if (curr_item.isbn () == item.isbn ()) {
13                 curr_item += item;
14                 ++cnt;
15             }
16             else {
17                 cout << curr_item << "\t\t\t: sales " << cnt << " times" << endl;
18                 curr_item = item;
19                 cnt = 1;
20             }
21         }
22         cout << curr_item << "\t\t\t: sales " << cnt << " times" << endl;
23     }
24     else {
25         cerr << "No data?!" << endl;
26         return -1;
27     }
28     return 0;
29 }
View Code

   小结

    本章介绍了C++语言的只是,使得可以编译、运行简单的C++程序。可以看到如何顶一个main函数,它是OS执行程序的调用入口。还有如何定义便来ing,如何进行输入、输出,以及如何编写if、for、while 语句。最后介绍了C++中最基本的特性--类。

    • 术语表
      • 参数(实参,argument)向函数传递的值
      • 赋值(assignment)抹去一个对象的当前的值,用一个新值取代之。
      • 程序块儿(block)0条或多条语句的序列。
      • 缓冲区(buffer)一个存储区域,用于保存数据。IO设施通常将输入(或输出)数据保存在一个缓冲区中,读写buffer的动作与程序中的动作是无关的。我们可以显示地刷新输出缓冲,以便强制将缓冲区(内存中存储的值)中的数据写入输出设备(显示器)。默认情况下,读 cin 会刷新 cout ;程序非正常终止也会刷新cout。
      • 内置类型(build-in tpye)由语言定义的类型,如int。
      • Cerr 一个ostream 对象,关联到标准错误,通常写入到与标准输出相同的设备(如显示器)。默认情况下,写到ceer 的数据是不缓冲的。cerr 通常用于输出错误信息胡哦其他不属于ucx正常逻辑的输出内容。
      • 字符串字面值常量(character string literal)术语 string literal 的另一种叫法。
      • cin 一个istream 对象,用来从标准输入读取数据(键盘)
      • 类(class)一种用于定义自定义数据结构及其相关操作的机制。类是C++中最基本的特性之一。标准库类型中,如istream 和 ostream 都是类。
      • 类类型(class type)类定义的类型,类名记为类型名。
      • clog 一个 ostream 对象,关联到标准错误。默认情况下,写到clog的数据是被缓冲的。clog 通常用于报告程序的执行信息,存入一个日志文件中。
      • 注释 (comment)被编译器忽略的出现文本。C++有两种类型的注释:单行注释和界定符对儿注释。
      • 条件(condition)求值结果为真或假的表达式。通常用值0表示假,用非0值表示真。
      • cout 一个ostream对象,用于将数据写入标准输出。通常用于程序的正常输出内容。
      • 花括号(curly brace)花括号用于划定程序块儿的边界。左花括号 { 为程序的开始,右花括号 } 为结束。
      • 数据结构(data structure)数据及其上锁允许的操作的一种逻辑组合。
      • 编辑 - 编译 - 调试(edit - compile - debug)是程序能正确执行的开发过程
      • 文件结束符(end - of - file)系统特定的标识,指出文件中没有更多数据了。Ctrl + D
      • 表达式(expression)最小的计算但愿。一个表达式包含一个或多个运算对象,通常还包含一个或多个运算符。表达式求值会产生一个结果。例如:假设i 和 j 是int 对象,则 i + j 是一个表达式,它产生两个int 值得和。

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:字符串查找

下一篇:Little Elephant and Elections CodeForces - 258B