跳转至

第I部分:C++基础

第 1 章 开始

1.2 初识输入输出

iostream 库包含两个基础类型istream 和 ostream,分别表示输入流和输出流。一个流就是一个字符序列,是从IO设备读出或写入IO设备的。术语“流”(stream)表达的是,随着时间推移,字符是顺序生成或消耗的。

标准输入输出对象

标准库定义了4个IO对象,标准输入-cin,标准输出-cout,标准错误(输出警告和错误信息)-cerr,输出运行时的一般性信息-clog

向流写入数据

输出运算符(<<)接受2个运算对象,左边是一个ostream对象,右边是需要打印的值,计算结果是写入给定数值的ostream对象。

“Enter two words.”是一个字符串字面值常量。

std::endl是一个称作操纵符(manipulator)的特殊值,其效果是结束当前行,并且将与设备关联的缓冲区(buffer)中的内容刷入设备中。缓冲刷新可以保证目前为止产生的输出都真正写入输出流中,而不是停留在内存中等待写入流。

使用标准库中的名字

std::指出名字cout和endl是定义在名为 std 的命名空间(namespace)中,命名空间可以帮助我们避免名字定义的冲突,以及使用库中相同名字导致的冲突。标准库定义的所有名字都在命名空间中。

副作用:使用标准库中的名字是,必须说明是使用 std 中的名字,通过 作用域运算符(::)来指出。

从流读取数据

同 写入 类同,略。

1.3 注释简介

注释界定符不能嵌套,可以用单行注释方式注释掉

1.4 控制流

当用一个istream对象作为条件时,效果是检测流的状态。如果流未遇到错误,那么检测错误。当遇到文件结束符(End-Of-File)或遇到无效输入,istream对象会变的无效,从而使得(循环中的)条件变假。

键盘输入文件结束符:Ctrl+Z→Enter/Return键;Ctrl+D

1.5 类简介

通过定义一个类(class)来定义自己的数据结构,一个类定义一个类型以及与其相关联的一组操作。

……略

文件重定向:$ addItems <infile >outfile 已经有编译名为addItems.exe的可执行文件,会从infill文件读取销售记录,并把输出结果写到outfile文件中。

成员函数是定义为类的一部分的函数,有时也被称为方法。item1.isbn()调用名为isbn的成员函数,使用**点运算符**来表达我们需要“名为item1的对象的isbn成员”。运算结果是右侧运算对象指定的成员。调用运算符()


命名空间(namespace)将库定义的名字放在一个单一位置的机制

第 2 章 变量和基本类型

2.1 基本内置类型

C++定义了一套包括算数类型(arithmetic type)和空类型(void)在内的基本数据类型。

2.1.1 算数类型

算数类型分为:整型(包括字符和布尔类型在内)和浮点型

算数类型的尺寸(所占比特数):有C++标准规定的尺寸的最小值,也允许编译器赋予更大的尺寸

基本字符类型~char。其他字符类型用于扩展字符集,类型char16_tchar32_t为Unicode字符集服务(Unicode是用于表示所有字符语言中字符的标准)

就符号而言,字符型被分为了三种:charsigned charunsigned char,且注意类型charsigned char并不一样。虽然有三种,但表现形式只有两种:带符号和不带符号。

2.1.2 类型转换

当我们赋给带符号类型一个超出它表示范围的值,结果是未定义的。程序可能会继续工作、崩溃也可能生成垃圾数。

unsigned int u = 10-42在此案例中,无符号整数通常是32位的,这意味着它们可以表示从0到4294967295的值,实际计算4294967296 + 10 - 42 = 4294967306 - 42 = 4294967264

表达式里同时有带符号类型和无符号类型,

2.1.3 字面值常量

默认情况下,十进制字面值是带符号数,八进制和十六进制字面值可能是符号或无符号的。

字符串字面值实际上是常量字符构成的数组,编译器会在每个字符串的结尾处添加空字符('\0')

两类不可直接使用字符:不可打印字符(退格、控制字符…)特殊含义字符。这些情况下要用到转义字符

反斜杠后面的八进制数字最多只有3个,超过3个不算在内;十六进制数用到后面跟到的所有数字。

**指定字面值的类型:**添加前缀或后缀

image-20241009105436233

2.2 变量

变量提供着一个具名、可供程序操作的存储空间。变量类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值的范围,以及变量能参与的运算。对于C++而言,变量和对象一般可以互换使用。

2.2.1 变量定义

string book("0-201-783450X")
int units_sold = 0;
int units_sold = {0};
int units_sold{0};
int units_sold(0);

**对象**是指一块能存储数据并具有某种类型的内存空间。

初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。

使用列表初始化(a{0})且初始值存在丢失信息的风险,编译器将报错:int a{2.34}

默认初始化:

内置类型的变量未被显式初始化,它的值由定义的位置决定。定义于任何函数体之外的变量被初始化为0。例外是,定义于函数体内部的内置类型变量将不被初始化,其值是未定一的,试图访问此类值将引发错误。

绝大多类都支持无需显式初始化而定义对象,这样的类提供了一个合适的默认值。

2.2.2 变量声明与定义的关系

C++语言支持分离式编译

声明使得名字为程序所知,定义负责创建与名字关联的实体。

变量声明和定义都规定了变量的类型和名字,除此之外,定义还申请存储空间也有可能为变量赋初始值。

C++始终静态类型语言,其含义是在编译阶段检查类型,检查类型的过程称为类型检查。

2.2.¾

作用域,标识符(用户、关键字、预定义-函数名、宏定义、类型名)

内层作用域、外层作用域

2.3 复合类型

2.3.1 引用

int ival = 1024;  
int &refVal = ival; // refVal指向ival  
int &refVal; // 错误,引用必须初始化  
  
int &refVal4 = 10; //错误,引用类型的初始值必须是对象  
double k = 1.0;  
int &refVal4 = k; //错误,此处引用类型的初始值必须是int型对象

定义引用时,程序把引用和它的初始值绑定在一起,而不是把初始值拷贝给引用。一旦初始化完成,就 一直 绑定在一起,无法重新绑定。

引用并非对象,所以不能定义引用的引用,只是为已存在的对象起一个 别名

refVal = 2; // 即赋值给ival
int ii = refVal; // 即 ii = ival

int &refVal3 = refVal; // 把refVal3绑定到ival
int i = refVal; // i 被初始化为ival的值

引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起。

案例:

#include <bits/stdc++.h>
using namespace std;

void swap(float &, float &);

int main(){
    float a=10,b=5;
    cout<<"输出调用前的a,b值"<<endl;
    cout<<"a="<<a<<" "<<"b="<<" "<<b<<endl;
    swap(a,b);
    cout<<"输出调用后的a,b值"<<endl;
    cout<<"a="<<a<<" "<<"b="<<" "<<b<<endl;
    return 0;
}

void swap(float &x, float &y){
    float temp;
    temp=x; x=y; y=temp;
    cout<<"输出x,y的值"<<endl;
    cout<<"x="<<x<<" "<<"y="<<" "<<y<<endl;
    return ;
}

2.3.2 指针

6.4 函数重载

如果同一作用域内几个函数名字相同但形参列表不同,我们称之为 重载(overloaded)函数

main函数不能重载。

不允许两个函数除了返回类型外其他所有的要素都相同,否则,第二个函数声明是错误的。

判断两个形参的类型是否相异

有时候两个形参列表看起来不一样,但实际相同:

Record lookup(const Account &acct);
Record lookup(const Account &); // 省略了形参的名字

typedef Phone Telno;
Record lookup(const Phone&);
Record lookup(const Telno&); //Phone和Telno的类型相同

可见,形参的名字仅仅起到帮助记忆的作用,有没有他不影响列表的内容。

在第二对声明中,类型别名只是为已存在的类型提供另外一个名字,并不是创建新类型,本质上没什么不同。

重载和const形参

……

6.5 特殊用途语言特性

6.5.1 内联函数

内联函数可避免函数调用的开销

将函数指定为 内联函数(inline),通常就是将它在每个调用点上“内联地”展开。

inline const string &shorterString(const string &s1, const string &s2){
    return s1.size() <= s2.size() ? s1 : s2;
}

那么对于cout << shorterString(s1,s2) << endl;将在编译过程中展开成类似以下形式:cout << (s1.size() <= s2.size() ? s1 : s2) << endl;

  • 内联函数体内不能有循环语句和switch语句。
  • 内联函数的声明必须出现在内联函数第一次被调用之前。
  • 对内联函数不能进行异常接口声明。