2 C++ 语法

1 变量

  变量提供了一个具名的、可供程序操作的存储空间。C++ 中的每个变量都有其数据类型,数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值的范围,以及变量能参与的运算。

1.1 变量的定义

  变量定义的基本形式:首先是类型说明符,随后紧跟由一个或多个变量名组成的列表,其中变量名以逗号分隔,最后以分号结束。列表中每个变量名的类型都由类型说明符指定,定义时还可以为一个或多个变量赋初值。例如:

int sum = 0, value, units_sold = 0;    // 3 个变量都是 int 类型的
std::string hello("你好");             // 变量 hello 通过一个字符串字面值初始化

1.1.1 初始值

  当对象在创建时获得了一个特定的值,我们说这个对象被初始化了。用于初始化变量的值可以是任意复杂的表达式。当一次定义了两个或多个变量时,对象的名字随着定义也就马上可以使用了。因此在同一条定义语句中,可以用先定义的变量值去初始化后定义的其它变量。例如:

double price = 109.99, discount = price * 0.16;    // 正确:price 先被定义并赋值,随后被用于初始化 discount

  很多程序员对于用等号=来初始化变量的方式倍感困惑,这种方式容易让人认为初始化是赋值的一种。事实上,在 C++ 语言中,初始化和赋值是两个完全不同的操作。虽然很多时候这种区别无关紧要。初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。

1.1.2 列表初始化

  C++ 定义了几种不同形式的初始化。例如,要想定义一个名为units_soldint变量并初始化为 0,有以下 4 种方法:

int units_sold = 0;
int units_sold = {0};
int units_sold{0};
int units_sold(0);

使用花括号来初始化的形式称为列表初始化。当用于内置类型的变量时,这种初始化形式有一个重要特点:如果我们使用列表初始化且初始值存在丢失信息的风险,即窄化,精度降低或造成数值变动,则编译器将报错,例如:

long double ld = 3.1415926536;
int a{ld}, b = {ld};              // 错误:转换未执行,因为存在丢失信息的危险
int c(ld), d = ld;                // 正确:转换执行,且确实丢失了部分值

1.1.3 默认初始化

  如果定义变量时没有指定初值,则变量被默认初始化,此时变量被赋予了“默认值”。默认值到底是什么由变量类型决定,同时定义变量的位置也会对此有影响。
  如果是内置类型的变量未被显式初始化,它的值由定义的位置决定。定义于任何函数体之外的变量被初始化为 0。定义于函数体内部的内置类型变量将不被初始化。一个未被初始化的内置类型变量的值是未定义的,如果试图拷贝或以其它形式访问此类值将引发错误。
  绝大多数类都支持无须显式初始化而定义对象,这样的类提供了一个合适的默认值。一些类要求每个对象都显式初始化,此时如果创建了一个该类的对象而未对其做明确的初始化操作,将引发错误。

2 控制流语句

2.1 条件判断语句

2.1.1 if语句

  if语句的作用是:判断一个指定的条件是否为真,根据判断结果决定是否执行另外一条语句。其语法形式有:

if (condition)
	statement
	
if (condition)
	statement
else
	statement2

condition必须用圆括号包围起来,其类型必须能转换成布尔类型。

2.2 循环语句

2.2.1 while 语句

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

while (condition)
    statement

while语句的执行过程是交替地检测condition条件和执行关联的语句statement,直至condition为假时停止。所谓条件就是一个产生真或假的结果的表达式。

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

  我们可以通过whileistream对象实现读取数量不定的输入数据。假设要对用户输入的一组数求和,我们预先不知道有多少个输入。这就需要不断读取数据直至没有新的输入为止:

#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对象作为条件时,其效果是检测流的状态。如果流是有效的,即流未遇到错误,那么检测成功。当遇到文件结束符,或遇到一个无效输入时,istream对象的状态会变为无效。处于无效状态的istream对象会使条件变为假。

2.2.2 for语句

2.2.2.1 传统的for语句

  每个for语句都包含两部分:循环头和循环体。循环头控制循环体的执行次数,它由三部分组成:一个初始化语句、一个循环条件、一个表达式。循环体每次执行前都会先检查循环条件。

2.2.2.2 范围for语句

  范围for语句遍历给定序列中的每个元素,并对序列中的每个值执行某种操作,其语法形式为:

for (declaration : expression)
	statement

其中,expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。例如,string对象表示一个字符的序列,因此string对象可以作为范围for语句中的expression部分:

std::string s("some string");
for (char c : s) {
	std::cout << c << std::endl;    // 输出当前字符
}

2.3 跳转语句

  跳转语句中断当前的执行过程。C++ 提供了 4 种跳转语句:breakcontinuegotoreturn

2.3.1 break语句

  break语句负责终止离它最近的循环语句或switch语句,并从这些语句之后的第一条语句开始继续执行。break语句只能出现在循环语句或switch语句的内部。

3 标准库类型string

  标准库类型string表示可变长的字符序列,使用string类型必须首先包含头文件#include <string>。作为标准库的一部分,string定义在命名空间std中。

3.1 定义和初始化string对象

  下表列出了初始化string对象的方式。

表:初始化string
语句描述
1string s1默认初始化,s1是一个空字符串。
2string s2(s1)s2s1的副本。

3.2 读写string对象

3.2.1 输入输出流

  输入输出流也可以读写string对象,例如:

string s;                       // 默认初始化空字符串
std::cin >> s;                  // 将 string 对象读入 s,遇到空白停止
std::cout << s << std::endl;    // 输出 s

在执行读取操作时,string对象会自动忽略开头的空白(即空格符、换行符、制表符等),并从第一个真正的字符开始读起,直到遇到下一处空白为止。因此,如果程序的输入是" Hello ",则输出将是"Hello",输出结果中没有任何空格。
  此外,也可以使用while语句实现读取未知数量的字符串对象,例如:

string word;
while (std::cin >> word) {                 // 反复读取,直至到达文件末尾
	std::cout << word << std::endl;        // 逐个输出单词,每个单词后面紧跟一个换行
}

3.2.2 使用getline()读取一整行

  有时我们希望能在最终得到的字符串中保留输入时的空白符,这时应该用getline()函数代替原来的>>运算符。getline()函数的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符为止(注意换行符也被读进来了),然后把所读的内容存入到那个string对象中去(注意不存在换行符)。getline()只要一遇到换行符就结束读取操作并返回结果,哪怕输入的一开始就是换行符。如果输入真的一开始就是换行符,那么所得的结果是个空string
  和输入运算符一样,getline()也会返回它的流参数。因此既然输入运算符能作为判断的条件,我们也能用getline()的结果作为条件。例如,我们要读取不定数量的行:

string line;
while (getline(std::cin, line)) {        // 每次读入一整行,直至到达文件末尾
	std::cout << line << std::endl;
}

因为line中不包含换行符,所以我们手动地加上换行操作符。

3.3 字符串的运算

3.3.1 拼接

  两个string对象相加(+)得到一个新的string对象,其内容是把左侧的运算对象和右侧的运算对象串接而成。复合赋值运算符(+=)负责把右侧string对象的内容追加到左侧string对象的后面。例如:

string s1 = "Hello, ", s2 = "World";
string s3 = s1 + s2;                // s3 的内容是 "Hello World"
s1 += s2;                           // 等价于 s1 = s1 + s2

  即使一种类型并非所需,我们也可以使用它,前提是该种类型可以自动转换成所需的类型。因为标准库允许把字符字面值和字符串字面值转换成string对象,所以在需要string对象的地方就可以使用这两种字面值来替代。当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string对象。例如:

string s4 = s1 + ", ";              // 正确:把一个 string 对象和一个字面值相加
string s5 = "Hello" + ", ";         // 错误:两个运算对象都不是 string
string s6 = s1 + ", " + "World";    // 正确:每个加法运算符都有一个运算对象是 string
string s7 = "Hello" + ", " + s2;    // 错误:不能把字面值直接相加

  警告:因为某些历史原因,也为了与 C 兼容,所以 C++ 中的字符串字面值并不是标准库类型string的对象。切记,字符串字面值与string是不同的类型。

3.4 常用方法

3.4.1 长度

s.size()

  返回字符串s中字符的个数,即对象的长度。

3.4.2 子字符串操作

s.substr(pos, n)

返回原始string对象的一部分或全部的拷贝。传递给substr()的是可选的开始位置和计数值。例如:

string s("Hello World");
string s2 = s.substr(0, 5);      // s2 = Hello
string s3 = s.substr(6);         // s3 = World
string s4 = s.substr(6, 11);     // s4 = World
string s5 = s.substr(12);        // 抛出一个 out_of_range 异常

如果开始位置超过了string的大小,则会抛出一个out_of_range异常。如果开始位置加上计数值大于string的大小,则会调整计数值,只拷贝到string的末尾。

  • 10
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值