C++ primer 第一章学习笔记

第一章 开始

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

​ 每个C++程序都包含一个或多个函数,其中有一个必须是main函数。

//最简单的main函数
int main()
{
	return 0;
} 

一个函数的定义包括四部分:

  • 返回类型
  • 函数名
  • 形参列表
  • 函数体

注意:

  • main函数的返回类型必须是 int 型(int类型是一种内置类型,即语言自身定义的类型)。

  • 当return语句包含一个值时,此返回值的类型必须与函数的返回类型相容。

    在大多数系统中,main的返回值被用来指示状态。返回值0表明成功,非0的返回值含义由系统定义,通常用来指出错误类型

1.1 编译、运行程序

程序源文件命名约定

  • 程序文件通常被称为源文件

  • 源文件的名字以一个后缀为结尾,后缀是一个句点后接一个或多个字符组成的。

    后缀告诉系统这个文件是一个C++程序,不同编译器使用不同的后缀命名约定,最常见的包括.cc、.cxx、.cpp、.cp 及 .C

从命名行运行编译器

见P3~P4

再探编译

编译器的一部分工作是寻找程序文本中的错误。编译器没有能力检查一个程序是否按照其作者的意图工作,但可以检查形式上的错误。下面是一些最常见的编译器可以检查出来的错误:

  • 语法错误

    //错误:main的参数列表漏掉了
    int main(
    {
        //错误:endl后使用了冒号而非分号
        std::cout << "Read each file" << std::endl:
        //错误:字符串字面值常量的两侧漏掉了分号
        std::cout << Update master. <<std::endl;
        //错误:漏掉了第二个输出运算符
        std::cout << "Write new master." std::endl;
        //错误:return语句漏掉了分号
        return 0
    }
    
  • 类型错误:C++中每个数据项都有其类型。例如:10的类型是int;单词“hello”,包括两边的双引号标记,则是一个字符串字面值常量。一个类型错误的例子:向期望参数为int的函数传递了一个字符串字面值常量。

  • 声明错误:C++程序中每个名字都要先声明后使用。名字声明失败通常会导致一条错误信息。两种常见的错误声明:

    • 对来自标准库的名字忘记使用 std::
    • 标识符名字拼写错误

按照编译器提示的报告顺序逐个修正错误是一种好习惯。因为一个单个错误常常会具有传递效应,导致编译器在其后报告比实际数量多得多的错误信息。

另一个好习惯是在每修正一个错误后就立即重新编译代码,或者最多是修正了一小部分明显的错误后就重新编译。这就是所谓的 “编辑-编译-调试” 周期。

1.2 初始输入输出

​ C++语言未定义任何输入输出IO语句,取而代之的是包含了一个全面的标准库来提供IO机制(以及很多其他设施)。

​ iostream 库包含两个基础类型 istream(输入流) 和 ostream(输出流)。

​ 一个流就是一个字符系列,是从IO设备读出或写入IO设备的。术语“流”想表达的是:随着时间的推移,字符是顺序生成或消耗的。

标准输入输出对象

​ 标准库定义了4个IO对象。

处理输入:

  • cin(istream类型的对象),该对象也被称为标准输入

处理输出:

  • cout(ostream类型的对象),该对象也被称为标准输出
  • cerr(ostream类型的对象),通常用 cerr 来输出警告和错误消息,因此也被称为标准错误
  • clog(ostream类型的对象),用来输出程序运行时的一般性信息

一个使用IO库的程序

示例:

​ 一个提示用户输出两个数,然后输出它们的和程序:

#include<iostream>
int main()
{
    std::cout << "Enter two number:" << std::endl;
    int v1 = 0, v2 = 0;
    std::cin >> v1 >> v2;
    std::cout << "The sum of " << v1 << "and" << v2
              << "is" << v1 + v2 << std::endl;
    return 0;
}

头文件

  • 第一行告诉编译器我们想使用iostream库。尖括号中的名字指出了一个头文件
  • 每个使用标准库设施的程序都必须包含相关的头文件
  • 对于来自标准库的头文件应用尖括号(< >)包围头文件名,对于不属于标准库的头文件,则用双引号(“ ”)包围
  • #include指令和头文件必须写在同一行
  • 通常,#include指令必须出现在所有函数之外,一般将一个程序的所有#include指令放在源文件的开始

向流写入数据

​ 在C++中,一个表达式产生一个计算结果,它由一个或多个运算对象和(通常是)一个运算符组成。
​ << (输出运算符)接受两个运算对象:左侧的运算对象必须是一个ostream对象,右侧的运算对象是要打印的值。此运算符将给定的值写到给定的ostream对象中。输出运算符的计算结果就是其左侧运算对象。

​ 对于语句std::cout << "Enter two number:" << std::endl;,使用了两次 << 运算符,因为此运算符返回其左侧的运算对象,因此第一个运算符的结果成为了第二个运算符。这样,就可以将输出请求连接起来。
​ 链中每个运算符的左侧运算对象都是相同的(本例中是:std::cout)。因此可以用两条语句生成相同的输出。

  • std::cout << "Enter two number:" << std::endl;
  • <=> (std::cout << "Enter two number:") << std::endl;
  • <=> std::cout << "Enter two number:"; std::cout << std::endl;

操纵符 endl

​ endl是一个被称为操纵符的特殊值。写入endl的效果是结束当前行,并将与设备关联的缓冲区中的内容刷到设备中。缓冲刷新操作可以保证到目前为止的所有输出都真正写入输出流中,而不是仅停留在内存中等待写入流。

在调试试我们经常添加打印语句。这类语句应保证“一直”刷新流。否则程序崩溃,输出可能还留在缓冲区中,从而导致关于程序崩溃位置的错误推断。

从流读取数据

​ 初始化一个变量,就是在变量创建的同时为它赋予一个值。

​ >>(输入运算符)与输出运算符类似,接受一个istream作为其左侧运算对象,接受一个对象作为其右侧运算对象。他从给定的istream读入数据,并存入给定对象中。与输出运算符类似,输出运算符返回其左侧运算对象作为其计算结果。

同理有:

  • std::cin >> v1 >> v2;
  • <=> (std::cin >> v1) >> v2;
  • <=> std::cin >> v1; std::cin >> v2;

使用标准库中的名字

  • 前缀std::指出名字 cout 和 endl 是定义在名为 std 的命名空间(namespace)中的。命名空间可以帮助我们避免不经意的名字定义冲突,以及使用库中相同名字导致的冲突。
  • 标准库定义的所有名字都在命名空间 std 中。
  • 通过命名空间使用标准库的副作用:当使用标准库中的一个名字时,必须显式说明我们想使用来自命名空间 std 中的名字。

1.3 注释简介

注意:当修改代码时,不要忘记同时更新注释!

C++中注释种类

​ C++中有两种注释:单行注释和界定符对注释。

  • 单行注释:以双斜线(//)开始,以换行符结束。这种注释可以包含任何文本。
  • 注释界定符:继承自C语言的两个界定符(/* 和 */)。可以包含除 */ 以外的任何内容。
    当注释界定符跨越多行时,最好能显式指出其内部的程序都属于多行注释的一部分。因此我们采用的风格是,注释内的每一行都以一个星号开头,从而指出整个范围都是多行注释的一部分
/*
* 注释对/* */不能嵌套。
* “不能嵌套”几个字会被认为是源码,
* 像剩余程序一样处理。
*/

1.4 控制流

语句块:用花括号包围的零条或多余语句的序列。

1.4.1 while语句

​ while语句反复执行一段代码,直至给定条件为假为止。

while语句形式:

while(condition)
	statement

1.4.2 for语句

​ 由于在循环条件中检测变量、在循环体中递增变量的模式使用非常频繁,以至于C++语言专门定义了第二种循环语句——for语句

​ 每个for语句都包括两部分:循环头和循环体。循环头控制循环体的执行次数,它由三部分组成:一个初始化语句(init-statement)、一个循环条件(condition)以及一个表达式(expression)。

  • 初始化语句只在for循环入口处执行一次
  • 循环体内次执行前先检查循环条件
  • 表达式在for循环体之后执行

1.4.3 读取数量不定的输入数据

​ 如 当我们预先不知道要对多少个数求和时,就需要不断读取数据直至没有新的输入为止:

#include<iostream>
int main()
{
    int sum = 0, value = 0;
    //读取数据直至遇到文件尾,计算所有读入的值的和
    while(std::cin >> value) {
        sum += value;
    }
    std::cout << "Sum is : " << sum << std::endl;
    return 0;
}

​ 当使用一个istream对象作为条件时,其效果是检测流的状态。

  • 如果流是有效的,即流未遇到错误,那么检测成功。
  • 当遇到文件结束符(end-of-file),或遇到一个无效输入时(如读入的值不是一个整数),istream对象的状态会变为无效。处于无效状态的istream对象会使条件变为假。

1.4.4 if语句

用if语句写一个程序,来统计在输入中每个值连续出现了多少次:

#include<iostream>
int main() 
{
	//currVal是我们正在统计的数;将读入的新值存入val
	int currVal = 0, val = 0;

	//读取第一个数,并确保确有数据可以处理
	if (std::cin >> currVal) {
		int cnt = 1; //保存正在处理的当前值个数

		while (std::cin >> val) { //读取剩余数字
			if (val == currVal)
				++cnt;
			else {
				std::cout << currVal << "occurs"
					<< cnt << "times" << std::endl;
				currVal = val; //记住新值
				cnt = 1; //重置计数器
			}
		}//while循环在这结束

		//记住打印文件中最后一个值的个数
		std::cout << currVal << "occurs"
			<< cnt << "times" << std::endl;
	}
	
	return 0;
}

1.5 类简介

  • 一个类定义了一个类型,以及与其关联的一组操作。

  • 习惯上,头文件根据其中定义的类的名字来命名。通常使用.h作为头文件的后缀,也有一些程序员习惯用.H、.hpp或.hxx

  • 标准库头文件通常不带后缀。

  • 编译器一般不关心头文件名的形式,但有的IDE对此有特定要求。

1.5.1 Sales_item类

​ 每个类都定义了一个新的类型,其类型名就是类名。因此,Sales_item 类定义了一个名为Sales_item 的类型。

​ 语句:Sales_item item;
​ 想表达的item是一个Sales_item类型的对象。通常将“一个Sales_item类型的对象” 简单说成”一个Sales_item对象“ 或更简单的”一个Sales_item“。

//Sales_item类
class Sales_item {
	// these declarations are explained section 7.2.1, p. 270 
	// and in chapter 14, pages 557, 558, 561
	friend std::istream& operator>>(std::istream&, Sales_item&);
	friend std::ostream& operator<<(std::ostream&, const Sales_item&);
	friend bool operator<(const Sales_item&, const Sales_item&);
	friend bool
		operator==(const Sales_item&, const Sales_item&);
public:
	// constructors are explained in section 7.1.4, pages 262 - 265
	// default constructor needed to initialize members of built-in type
#if defined(IN_CLASS_INITS) && defined(DEFAULT_FCNS)
	Sales_item() = default;
#else
	Sales_item() : units_sold(0), revenue(0.0) { }
#endif
	Sales_item(const std::string &book) :
		bookNo(book), units_sold(0), revenue(0.0) { }
	Sales_item(std::istream &is) { is >> *this; }
public:
	// operations on Sales_item objects
	// member binary operator: left-hand operand bound to implicit this pointer
	Sales_item& operator+=(const Sales_item&);
 
	// operations on Sales_item objects
	std::string isbn() const { return bookNo; }
	double avg_price() const;
	// private members as before
private:
	std::string bookNo;      // implicitly initialized to the empty string
#ifdef IN_CLASS_INITS
	unsigned units_sold = 0; // explicitly initialized
	double revenue = 0.0;
#else
	unsigned units_sold;
	double revenue;
#endif
};

类定义了行为:

一般而言,类的作者决定了类类型对象上可以使用的所有操作(如Sales_item的作者定义了类对象可以执行的所有动作)。

1.5.2 初始成员函数

代码:

#include<iostream>
#include"Sales_item.h"
int main()
{
    Sales_item item1,item2;
    std::cin >> item1 >>item2;
    //首先检查item1和item2是否表示相同的书
    if(item1.isbn() == item2.isbn()) {
        std::cout <<item1 + item2 <<std::endl;
        return 0; //表示成功
    } else {
        std::cerr << "Data must refer to same ISBN" << std::endl;
        return -1; //表示失败
    }
}

上述程序中,if语句检测条件item1.isbn() == item2.isbn(),调用名为 isbn 的成员函数。

  • 成员函数是定义为类的一部分的函数,有时也被称为方法

  • 使用点运算符(.)来表达 某个对象 的 成员 。点运算符只能用于类类型的对象。其左侧运算对象必须是一个类类型的对象,右侧运算对象必须是该类型的一个成员名,运算结果为右侧运算对象指定的成员。

  • 使用调用运算符( () )来调用一个函数。

1.6 书店程序

见P21~ P22

上一篇:第2节课Matplotlib案例及作业


下一篇:python案例分析之电商销售数据分析