C++ PrimerPlus 学习笔记(一)基础与数据

文章目录

文件

新式C++头文件没有后缀名,例:

#include<iostream>
...
using namespace std;

使用不带后缀的头文件时注意要声明名称空间


c++的main函数
  • c++的main函数返回值类型应该是 int ,但main函数可以不写 return 0; 默认添加 return 0;
  • 常规的独立c++程序都有main函数,除非一些程序框架有其他启动函数,如 _tmain() ,实际上也是被一个隐藏的main函数调用了_tmain()

名称空间
方法一(一次性声明全部):
using namespace std;
方法二(逐个声明):
using std::istream;
using std::ostream;
using std::cout;
using std::endl;

注意:方法一和方法二都可以在函数定义中声明,这样只有这个函数可以使用该名称空间
名称空间是为了区分名称相同的标识符,若使用方法一和方法二,则不能再出现同名称的标识符。

方法三(使用时声明):
std::cout<<"Hello World"<<std::endl;

不能把空格,制表符,回车放在元素(比如名称)中间,也不能把回车放在字符串中。
(c++11 的原始(raw)字符串可包含回车)

初识输入输出流
endl

endl —— 操作符。

  • 效果:结束当前行,并将与设备关联的缓冲区中的内容刷到设备中。

  • 缓冲刷新操作可以保证目前为止程序所产生的所有输出都真正写入输出流中,而不是仅停留在内存中等待写入流

  • 打印语句时应该保证“一直”刷新流。C++Primer_P6.

  • 换行细节 ‘\n’ 与 endl 的差别

    endl 确保程序继续运行前刷新输出(立即显示到屏幕上),’\n’ 没有这样的保证。
    (在某些系统中,有时才会可能出现这种差别,比如在输入信息后才出现提示)

将istream对象作为条件
  • 效果是 检测流的状态
  • 如果流有效(未遇到错误),则检测成功
  • 当遇到 文件结束符(end-of-file,EOF),或遇到一个无效输入时(例如读到的值不是一个整数) 时,istream对象状态会变为无效,条件变假。
  • Windows 系统的输入文件结束符的方法是 Ctrl+Z,然后按 Enter键 。
  • Unix系统/Mac OS X系统中,文件结束符是Ctrl+D
不合法输入

例如:

int i;
cin>>i;
//但输入了字符,就会导致不合法输入

有的不合法输入会导致禁止读取输入,可通过cin.clear() 重置输入,并注意读取不合法的那些输入。

文件重定向

大多数操作系统支持文件重定向,该机制允许我们将标准输入和标准输出与命名文件关联起来:

在 cmd 中:

Test.exe <infile>outfile
# 或
Test <infile>outfile

#infile 是输入文件,outfile是输出文件
数字输出格式setf
  • 方法调用 setf(ios_base::fixed)将对象置于使用定点表示法的模式
  • 方法调用 setf(ios_base:showpoint)将对象置于显示小数点的模式,即使小数部分为0
  • 方法调用 precision 指定对象显示多少位小数
  • 方法width()设置下一次输出操作使用的字段宽度。默认为0,即刚好能容纳要显示的内容。
赋值——从右往左进行
标识符区分大小写
奇闻——main 不是关键字

由于它不是语言的组成部分,然而它是必不可少的函数的名称,可以把main作为变量名,但可能出错(在某种神秘的情况下)。

避免C++标准库的名字
  • 用户自定义的标识符中不能连续出现两个下划线
  • 也不能以下划线紧接大写字母开头
  • 定义在函数体外的标识符不能以下划线开头

一,数据

0.注意
  • c++允许在程序的任何地方声明新变量

    在首次使用变量前声明它。

  • 以 两个下划线 或 下划线+大写字母 打头的名称被保留给(编译器及其使用的资源)使用。以 一个下划线 开头的名称保留给实现,用作全局标识符。

    (“实现”可以理解为被c++内部使用,编程时尽量别用)

  • 内置类型的机器实现

    • 可寻址的最小内存块称为“字节(byte)”

    • 存储的基本单元称为“字(word)”

    • 大多数字节为8bit,字为4/8字节

1. 整型
1.1 类型规格
整型类型含义大小
short短整型至少16位,64位win7为16位
int整型至少与 short 一样长,64位win7为32位
long长整型至少32位,且至少与 int 一样长,64位win7为32位
long long (c++11新增)长整型长整型至少64位,且至少与 long 一样长,64位win7为64位
bool布尔类型未定义,取值true/flase
char字符至少8位
wchar_t宽字符至少16位
char16_tUnicode字符至少16位
char32_tUnicode字符至少32位
float单精度浮点型6位有效数字
double双精度浮点型10位有效数字
long double扩展精度浮点型10位有效数字
  • sizeof 运算符,对类型使用需带括号,对变量名括号可选
  • 头文件 climits 定义了许多符号常量来表示各类型的限制(大小,最大值,最小值),如 INT_MAX为int的最大取值。
1.2 初始化变量

c/c++ 的初始化

int v1 = 16; 
int v2(16);//c++ 的初始化

c++11 的列表初始化,用于结构和数组

int hamburgers = {24};//单值变量的初始化,c++98也能用
//c++11:
int var1{7};
int var2{};//var2=0 大括号里没有东西,被初始化为0

c++11 使得大括号初始化器 用于任何类型(等号可选)

列表初始化的注意:

  • 如果列表初始化且初始值存在丢失信息的风险,则编译器将报错:

    long double ld = 3.1415926536;
    int a{ld};//Error!存在丢失信息的风险
    int c(ld),d=ld;//OK! 执行转换(确实丢失了部分)
    

(列表初始化待详解)

1.3 无符号类型
1.4 选择整型类型

即使系统上int 与 long 都是32位,在需要表示的值大于16位最大值时,仍要用 long,便于移植。

1.5 进制

进制表示:

012            八进制     =十进制的10
0x12           十六进制   =十进制的18

进制输出:

控制符进制
dec十进制
hex十六进制
oct八进制

控制符在名称空间 std 中
例:

int var = 42;
cout<<hex;
cout<<var<<endl;

输出 2a

1.6 c++ 如何确定字面值常量的类型

除非有理由将常量存储为其他类型(用户用后缀指定,或 值太大以至于不能用 int),否则默认就存为 int 型。

常量后说明
l 或 Llong 常量
u 或 Uunsigned int 常量
ul,uL,Ul,ULunsigned long 常量
ll 或 LL (c++11)long long 常量
ull …(c++11)unsigned long long 常量
12L

对于不带后缀的常量:
对十进制整数:
使用 int,long,long long 中能够存储该常量的最小类型
对十六进制或八进制整数:
使用 int,unsigned int long,unsigned long,long long 或 unsigned long long 中能够存储该常量的最小类型

1.7 char 型

八位,通常用于存储字符

  • cout.put() 输出一个字符。
  • 可以基于字符的八进制和十六进制ASCLL 码来使用转义序列:
    比如 Ctrl+Z 的 ASCLL 码为26,八进制 032,十六进制 0x1a ,可以用转义序列表示为 ‘\032’ 或 ‘\x1a’。
  • 字符型被分为 char,signed char 和 unsigned char 型。
  • char 和 signed char 并不一定一样。
  • 字符型有三种,但字符的表现形式只有两种,char实际上表现为其他两种中的一种,具体有编译器决定。
  • 通用字符名,用时再学(P52) (不常用)
    \u + 8个16进制位 或 \U + 16个16进制位用于表示各种字符集
  • char 型在c++中默认既不是有符号也不是无符号,可显示设定signed char ch1; unsigned char ch2;
  • wcha_t 宽字符型,
    • 16位的字节,用于表示拓展字符集(原 char 8位字节 表示基本字符集)
    • 用 wcin 和 wcout 处理 wcha_t 流。
    • 可用 前缀 L 表示宽字符常量和宽字符串
  • c++11 新增类型—— char16_t 和 char32_t
    • 都是无符号型
    • char16_t 的字符常量和字符串常量 前缀用 u,与\u + 8个16进制位匹配。
    • char32_t 的字符常量和字符串常量 前缀用 U,与\U + 16个16进制位匹配。
1.8 bool 型
  • true —— 真
  • false —— 假
  • true 和 false 都可以通过提升转换为 int 类型,true 为1,false 为0.
  • 任何数字值或指针值都可以被隐式转换为 bool ,非0为true,0为false
2. 浮点数
2.1 字面值常量浮点数表示法

(1)直接 3.14
(2)E 表示法(E的大小写都行)

底数E指数
3.14e+8//(3.14*10的8次方)
5.38E24
9.11e-31
2.2 浮点类型
浮点类型有效位数(2进制)
float至少32位,通常32位
double至少48位,且不少于 float,通常64位
long double至少和 double 一样多,通常为80,96,128位
  • 这三种类型有效位数可以一样多,通常不一样
  • 这三种类型的指数范围至少是-37到37
  • 可以从头文件 cfloat 或 float.h 中找到系统的限制。(有些c++实现 尚未添加该头文件)
  • 通常 float 有7位有效数字,double 有16个有效
2.3 精度

ostream 中的 setf() 方法迫使输出使用定点表示法,防止程序把较大的数切换为 E表示法,并使程序显示小数点后6位,参数 ios_base::fixed 和 ios_base::floatfield 是iostream 提供的常量。(没有 ios_base 就 ios)

#include<iostream>
int main(  )
{
	using namespace std;	
	
	cout.setf(ios_base::fixed,ios_base::floatfield);	
	
	float fvar=10.0/3.0;
	double dvar=10.0/3.0;
	const float million = 1.0e6;	
	
	cout<<"fvar = "<<fvar<<endl;
	cout<<"fvar*million = "<<fvar*million<<endl;
	cout<<"10*fvar*million = "<<10*fvar*million<<endl;	
	cout<<"dvar = "<<dvar<<endl;
	cout<<"dvar*million = "<<dvar*million<<endl;	 	
	
	return 0;
 }

输出:

fvar = 3.333333
fvar*million = 3333333.250000//精度不够
10*fvar*million = 33333332.000000//精度不够
dvar = 3.333333
dvar*million = 3333333.333333

程序说明:

  • 通常cout会删除结尾的0,但是cout.setf() 覆盖这种行为。
  • float 的精度比 double 低,确保float 至少有6位有效位,确保double 至少15位有效位。
2.4 指定字面值浮点常量类型
  • 默认浮点常量为 double 型。
  • 可通过后缀指定常量存储类型,f或F为float,l或L为 long double
2.5 浮点数优缺点
  • 优: 由于有缩放因子,他们表示的范围大得多
  • 缺:浮点运算通常比整数运算慢,且精度将降低,例:
#include<iostream>
int main(  )
{
	using namespace std;
		
	float fvar1=2.34e22f;
	float fvar2=1.0+fvar1;
	
	cout<<"fvar1 = "<<fvar1<<endl;
	cout<<"fvar2 = "<<fvar2<<endl;	
	cout<<"fvar2 - fvar1 = "<<fvar2-fvar1<<endl; 
	
	return 0;
 } 

输出:

 fvar1 = 2.34e+022
 fvar2 = 2.34e+022
 fvar2 - fvar1 = 0

原因:float 只能表示数字中的前6位或前7位,+1是在小数点左边第23位,不会对其有任何影响。

3.如何选择类型
  • 明确数值不可能为负时,选择无符号整型
  • 使用 int 执行整数运算,int 不够就long long,因为long一般与int一样大
  • 算术表达式中不要用 char/bool。因为char可能是无符号/有符号。
  • 如果要用char作为整型使用,明确指出时signed char还是unsigned char。
  • 执行浮点运算使用 double。因为 float 通常精度不够而两者的计算代价相差无几。long double 一般没必要,且其消耗不小。
4.常量
字面值常量

比如 42

[外链图片转存失败(img-MqengVuD-1568986200716)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1568206529570.png)]

转义序列
换行符 \n反斜线 \\
横向制表符 \t纵向制表符 \v
回车符 \r退格符 \b
进纸符 \f报警(响铃)符 \a
问号 ?双引号 \"
单引号 \'
  • 这些转义字符被当做一个字符使用

  • 也可以使用泛化的转义序列

    // \x后紧跟1个/多个 十六进制数字
    // \后紧跟1个/2个/3个 八进制数字
    //数字部分表示字符对应的数值
    //对于 Latin-1字符集(向下兼容ASCII):
    \7(响铃) \12(换行符)  \40(空格)
    \0(空字符) \115(字符M)  \x4d(字符M)
    
  • 像使用普通字符一样使用转义序列

    std::cout<<"Hi \x4dO\115!\n";
    //输出  Hi MOM! 
    
    
const 限定符
  • 限定该变量为常量,通常常量名称首字母大写,而用#define 宏定义的常量 全部名称大写。
  • 创建常量尽量用 const (指明了类型,而且作用域可限定在特定的函数或文件)
  • 可放在头文件中而不用担心多次定义
  • 在c++中(c不能),可用 const 的常量用作声明数组长度。
  • const 的常量只能在初始化时赋值
  • 若编译器编译时已知const初始化的值,他甚至可以不给const常量分配内存,而是类似宏定义一样替换
const int Months = 12;
const int num[Months]{1,2,3,4,5,6,7,8,9,10,11,12};;
  • 具体其链接性见“内存模型 cv-限定符”
  • 指针与const 见“指针”
  • 引用与const 见“函数-引用变量”
常量表达式
  • 值不会改变并且在编译过程就能得到计算结果的表达式
  • 字面值常量就是常量表达式,初始化的const对象也是。
constexpr 变量(C++11)
  • 将变量声明为constexpr类型以由编译器来验证变量的值是否是一个常量表达式。

  • 声明为 constexpr 的变量一定是一个常量且必须用常量表达式初始化

    constexpr int mf =20;
    constexpr int sz =mf +1;
    constexpr int sz = size();//size() 必须是一个constexpr函数
    
  • constexpr 函数待补充(C++Primer P214)

  • 一般来说,如果认为变量是一个常量表达式,就把它声明为constexpr

字面值类型
  • 初始化 constexpr变量所用到的类型
  • 算术类型,引用,指针都属于字面值类型
  • 有一种函数定义的有效范围超出函数本身的变量,其有固定地址,可供 constexpr的指针/引用 初始化。(C++Primer P185)
指针与constexpr
  • 指针/引用都能定义成constexpr,但是初始值受限

  • constexpr 的指针初始值必须是 nullptr/0,或者存储于某固定地址的对象(全局对象/静态对象/…)

  • 只需所指对象的地址固定,而无需对象的值固定!

  • constexpr 仅对指针有效,与所指对象无关(强行顶层const)

    constexpr int *p = nullptr;//p是常量指针
    
5. c++ 算术运算符
  • 浮点数和整型都是算术类型
    …+,-,*,/,%
6.类型转换

隐式转换(c++自动进行的转换);

  • 不同算术类型之间的赋值
  • 表达式包含不同类型时
  • 参数传递
6.1 初始化或赋值时

值将被转换为接收变量的类型
可能会出现的问题;

转换潜在问题
将较大的浮点类型转换为较小的浮点类型,如double→float精度(有效位)降低,值超出范围后结果不确定
将浮点类型转换为整型小数部分丢失,值超出范围后结果不确定
赋给无符号类型一个超出他表示范围的值。如-1赋给unsigned char->255超出范围后,通常只复制右边的字节(所赋值%该类型的表示范围)
赋给带符号类型一个超出他表示范围的值结果未定义,程序可能崩溃…
6.2 以 {} 方式初始化进行的初始化(c++11)(也叫 列表初始化)

不允许从较大类型到较小类型的转换(缩窄narrowing)

char c1{31352}; //错误,不能缩窄
char c2=31352;
6.3 表达式中的转换

c++会执行两种自动转换:

  • 1) 计算表达式时将bool,char,unsigned char,signed char,short 值转换为 int。(true=1,false=0)
    • 若short 比 int 短,则 unsigned short 会转换为 int ;若两者宽度相同,则 unsigned short 会转换为 unsigned int 。
    • wchar_t 被提升为下列类型中第一个宽度足够存储 wchar_t 取值范围的类型:int,unsigned int,long,unsigned long 。
  • 2 )一些不同类型同时出现时会自动化转换。C++11 校验表:
    • 若有 long double,则另一个操作数转换为 long double。
      • 否则,若有 double , 则另一个被转化为 double。
        • 否则,若有 float , 则另一个被转化为 float。
          • 否则,说明操作数都是整型,进行整型提升。
            • 都是整型时,若都有符号或都无符号,则低转换为高。
              • 若无符号级别高于有符号,则有符号转换为无符号的(unsigned int +char 则 char 转换为 unsigned int)。
                • 若有符号级别高于无符号,且有符号的级别可以表示无符号的所有取值,则无符号转换为有符号的(unsigned char + int 则 unsigned char 转换为 int)。
                  • 若有符号与无符号级别相同,则转换为无符号(unsigned int + int 则 int 转换为 unsigned int)
6.4 传递参数时的转换

一般由函数原型控制,也可以取消这种控制。
取消该控制后,对 char 和 short(signed和unsigned)整型提升,float→double。

6.5 强制类型转换(显式)

不会修改变量本身,而是创建一个新的,指定类型的值,该值可以在表达式中使用。

(typeName)value // from c
typeName(value) //c++

(第15章详说)

6.6 切勿混用带符号类型和无符号类型
auto 声明(c++11 )
  • 通过初始值自动推断类型
  • 所以要求必须初始化
auto n = 100; //n is int
auto x = 1.5; //x is double
auto y = 1.3e12L; //y is long double
auto i = 0,*p = &i;//基本类型必须一样

以上例子尽量别用,auto 用于其他作用

其真正含义待以后学习。(STL 中)

  • 编译器推断 auto 类型可能和初始值类型并完全一样,会适当调整:

  • 初始值为引用类型,则实际判断类型为被引用的类型

  • auto 一般会忽略顶层const,而保留底层const

    const int ci = i,&cr =ci;
    auto b = ci;//c是整型
    auto c = cr;//c是整型
    auto e = &ci;//e是指向整型常量的指针
    
  • 若希望是顶层const,则

    const int ci = 0;
    const auto f = ci;
    
  • 引用设为 auto:

    • 保留初始值的顶层const
    • 如果初始值也是引用,则相当于初始值替换成了被引用的那个原对象
    auto &g = ci;//g是一个整型常量引用,绑定ci
    //↑↑↑↑↑保留了ci的顶层const
    auto &h = 42;//Error!
    const auto &h = 42;//Right!
    
  • 一条语句定义多个变量时,初始值必须是同一种类型,包括是否const

    int i = 0;
    const int ci = 42;
    auto k = ci,&l = i;//k是整数型,l是整数型引用
    auto &m = ci,*p = &ci;//m是整数引用,p是指向常量的指针
    
    auto &n = i,*p2 = &ci;//Error!i是int,而ci是const int
    
  • auto 作用于数组名是效果为指针

decltype 类型指示符(C++11)
  • 目的:从表达式推断要定义的变量的类型,但是不以该表达式的值初始化

  • 选择并返回操作数的数据类型(不会实际计算其值)

  • decltype(f()) sum = x;//x的类型就是函数f的返回类型
    
  • decltype 与 引用

    int i =42,*p =&i,&r = i;
    decltype(r+0)b;//b是 int 型
    decltype(r)b1;//b1是 int& 引用
    decltype(*p)b2;//b2是 int& 引用 *p就是对象本身r
    
  • 表达式的形式影响

    int i =42;
    decltype(i) d;//d是一个int
    decltype((i)) e;//Error!! e是一个int&,必须初始化
    //注意:
    //decltype((variable))永远是引用
    //decltype(variable)只有 variable本身是引用才能使引用
    
  • 赋值是会产生引用的一类典型表达式

  • decltype 对于数组,效果还是数组

    int ia[] = {0,1,2,3,4,5,6,7,8,9};
    auto ia2(ia);//ia2是int指针
    decltype(ia) ia3;//ia3是10个元素的数组
    
科普:静态类型语言C++

C++是一种静态类型语言,即在编译阶段进行类型检查,这有助于提前发现问题。

建议:在第一次使用变量时再定义它

二,复合类型

2.1 数组(const 的 指针)
2.1.1 数组的初始化
int card[4]={3,6,8,10};
long hand[12]={1};//hand[0]=1,hand[i]=0,i=1,2,..,11
double dog[]={1,2,3,4}//可省略 [] 内,由编译器计算
double earnings[4]{1.2e4,1.6e5,1.1e4,1.7e4};// c++11
int cards[4]={};//all elements set to 0
int cardss[4]{};//all elements set to 0
char slifs{'h','h',311111}//error! 禁止缩窄转换
long slifs{12,10000,1.2}//error! 禁止缩窄转换(浮点到整型都是缩窄)
2.1.2 复杂数组声明
int *ptrs[10];
int &refs[10]; //Error!不存在引用的数组
int (*Parray)[10] = &arr;//Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr; //arrRef引用一个含有10个整数的数组
  • 对数组而言,应该从内向外阅读(数组名开始,然后右边,然后左边)
2.1.3 数组下标与指针下标
  • 通常将下标定义为 size_t 类型,是一种机器相关的无符号类型

  • 但是内置的下标运算符可以处理负数

    int *p = &ia[-2];
    int j = p[2];// j是 ia[0]
    
2.1.4 decltype 和 auto 对数组
int ia[] = {0,1,2,3,4,5,6,7,8,9};
auto ia2(ia);//ia2是int指针
decltype(ia) ia3;//ia3是10个元素的数组
指针也是迭代器
标准库函数 begin 和 end(C++11)
  • 定义在 iterator头文件 中
int ia[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = begin(arr);
int *last = end(ia);//尾后指针
  • 注意:尾后指针不能执行解引用和递增操作
指针运算
auto m = end(arr) - begin(arr);
//n是数组元素个数
  • 两个指针相减的结果是 ptrdiff_t 类型的标准库类型,是有符号类型

遍历数组的又一种方法:

int *b = arr,*e = arr+sz;
while(b<e)
{
	//use *b
	++b;
}
  • 指针运算同样适用于空指针和所指对象非数组的指针
  • 如果 p 是空指针,允许给p加上/减去 一个值为0的整型常量表达式
  • 两个空指针可以彼此加减,结果是0
多维数组
  • 多维数组每行首元素初始化
int ia[3][4] = {{3},{1},{2}};
//3 0 0 0
//1 0 0 0
//2 0 0 0
  • 多维数组的范围for循环
    • 总之出最内层不一定用auto引用外,其余层都必须用引用
int p[3][4] = {{1},{2},{3}};
for(auto &i:p){   //注意这一层必须引用,否则会把数组类型转换成指针
    //i 类型为 int(&i)[4]
   for(auto k:i)//如果要修改则这一层也要引用
   {//k就是int型
      cout<<k<<"\t"; 
    }
    cout<<endl;
}
2.2 字符串
2.2.2 字符串初始化
char char1[20]="ni hao a ";
char char2[]={"da jia hao a "};
char char3[]{"wo hen hao"};
2.2.1 拼接字符串常量

c++ 中 任何两个由空白(空格,制表符,换行符)分割的字符串敞亮都将自动拼接成一个。

cout<<"Hello " "World!""\n";
2.2.2 字符串输入
char name[20];
cin>>name;

cin 判断一个字符串输入结束的方式:空白(空格,制表符,换行符)。

#include <iostream>
int main(int argc, char** argv) {
	using namespace std;
	char ch;
	int count=0;
	cout<<"Enter a string end with '#'\n";
	cin>>ch;//输入字符串后会保存在输入流中进入while循环后逐个读取
	while(ch!='#')
	{
		cout<<ch;
		count++;
		cin>>ch;
	}
	cout<<endl<<"The length of string you input is: "<<count<<endl;
    system("pause");
	return 0;
}
//结果:
Enter a string end with '#'
ss ss   sss#
sssssss
The length of string you input is: 7
请按任意键继续. . .
2.2.3 每次读取一行字符串输入

cin.getline() 和 cin.get()

(1)面向行的输入 cin.getline()

  • 它通过回车键输入的换行符来确定输入结尾,
  • 参数:
    • 参数1:用来存储字符串的数组名称。
    • 参数2:要读取的字符数(包括\0,如该参数20则只能写入19个字符)
  • 不保存换行符!读取换行符并丢弃换行符。

(2)面向行的输入 cin.get()
该函数有多个变体,其中一种变体:
参数与 cin.getline() 一样,只是不再读取换行符,而是把它留在输入队列中。

还有一种不带参数的变体:读取下一个字符(即便是换行符)

可用来处理换行符

char name[20],address[40];
cin.get(name,20);
cin.get();
cin.get(address,40).get();//cin.get() 返回 cin对象
<=>
cin.getline(name,20).getline(address,40);//cin.getine() 返回 cin对象,从左往右执行

(3)空行 和 其他问题

cin.get() 读取空行后将设置失效位(failbit),使接下来的输入被阻断,可用 cin.clear() 来恢复输入。
cin.getline()会使输入从空行之后开始输入。

另一个问题:输入字符串比分配的空间(指定的第二参数)长。cin.geiline() 和cin.get() 会把余下的字符留在输入队列中,cin.getline() 还会设置失效位,关闭后来的输入。

详说 见5,6,17 章

2.2.4数字与字符串混合输入

(cin>>year).get(ch);

2.3 String 类

包含头文件<string>
名称空间 std

#include<iostream>
#include<string>
using namespace std;
int main()
{
	char char1[20]="ni hao a ";
	string str1="da jia hao a";
	
	cout<<char1<<endl<<str1<<endl;
	cin>>str1;
	cout<<str1[0]<<endl;		
	return 0;	
}

运行结果:

ni hao a
da jia hao a
hello
h

2.3.1 string 对象初始化

可以和C-风格字符串一样的初始化形式,或用C-风格字符串 初始化。

string str1="ni hao a ";
string str2={"da jia hao a "};
string str3{"wo hen hao"};
2.3.2 string 对象 赋值,拼接和附加
str1=str2;//可用 str 对象给 str对象赋值
str3 = str1 + str2;
str1+=str2; 
2.3.3 string 类 IO
  • string 对象的初始长度默认为0,可自动调整长度。

  • getline(cin,str); 类似于 cin.getline() 但是不用指明长度。(详见 二,文本输入-getline())

    • 该函数参数是一个输入流和一个string对象
    • 函数从给定的输入流中读入内容,直到遇到换行符为止(换行符也读进来了,但是被丢弃了),然后将所读内容存入 string对象(不存换行符)
    • 该函数一遇到换行符就返回
    • 返回值是流参数
2.3.4 string的empty与size方法
  • empty 根据string对象是否为空返回一个对应的布尔值
  • size 返回string对象的长度(字符个数)
2.3.5 string::size_type 类型
  • string标准库定义的几种配套类型之一
  • 是一个无符号的整型
  • 可用auto/decltype推断
  • 注意不要混用无符号数和有符号数
  • 是 size方法返回的类型
  • 是 [] 运算符的参数类型
2.3.6 注意检查[]的下标是否合法!
2.3.7 其他形式的字符串字面值

(1)其他编码

wchar_t title[] = L"Chief Astogator";
char16_t name[] = u"Felonia Ripova";
char32_t car[] = U"Humber Super Snipt";

c++11 还支持 Unicode 字符编码方案 UTF-8 。该方案中字符可能存储为1~4 个字节,c++使用前缀 u8 表示这种类型的字符串的字面值。

(2)原始字符串(raw)
字符表示的就是字符自己。
R"( … )"

cout<<R"(Jim "King" Tutt uses "\n" instead of endl)"<<"\n";
cout<<R"+*("""(Who wouldn't?)",she whispered.)+*"<<endl;

原始字符串语法允许在 “ 和 ( 之间添加其他字符,但必须在结束的 ) 和 " 之间添加同样的字符。

可将前缀 R 与其他字符串前缀结合使用,例如 Ru 或 uR 。

2.3.8 string类与C风格字符串的接口
  • 初始化可以使用结尾为 ‘\0’ 的字符数组

  • string对象的等号右边可以是C风格字符串

  • .c_str 返回C风格字符串

    • 返回结果是一个指针,指向C风格字符串

    • 结果指针类型是const char*

    • 无法保证返回的该字符串一直有效,可能C被修改后就无法使用

    • 所以应该把返回的内容拷贝一份以供使用

      string s("Hello World");
      const char *str;
      strcpy(str,s.c_str());
      
2.4 结构 struct
2.4.1 定义结构
struct produces
{
	char name[20];
	int price;
}...
struct produces dress;
produces hat;

c++ 允许在定义结构变量时 不用 struct 而直接使用结构名。

2.4.2 结构变量初始化
struct produces
{
	char name[20];
	int price;
}bag={"bag",15},bigbag={"big bag",32}...
produces hat =
{
	“Glorious Gloria”,
	18
};
produces apple{"apple",6};//C++11
produces mayor{};//所有成员被设为0
2.4.3 其他结构属性

可作为参数

可互相赋值

可没有结构名而直接定义结构变量:

struct
{
	int x,
	int y
}position;
2.4.4 结构数组
produces bags[2]=
{
	{"small bag",15},
	{"big bag",32}
};
2.4.5 结构中的 位字段

字段类型为 整型 或 枚举,然后是 冒号,然后是它指定了使用的位数,可以使用没有名称的字段来提供间距,每个成员都被称为 位字段(bit field)

struct torgle_register
{
	unsigned int SN:4;
	unsigned int :4;
	bool goodIn :1;
	bool goodTorgle:1;
}

初始化:

torgle_register tr={14,true,flase};
2.5 共用体 union
union one4all
{
	int int_val;
	long long_val;
	double double_val;
};

2.6 枚举 enum
enum spectrum{blue,orange,red,yellow};

从0开始作为其符号常量的值,又称为枚举量。

可用枚举来声明这种类型的变量:

spectrum color = blue;
  • 枚举量是整型,可被提升为 int ,但 int 不能自动转换为枚举整型。
  • 把非枚举变量赋给枚举变量视为错误。
  • 枚举 只定义了赋值运算符,没有为枚举定义算术运算。
    color = blue + orange;错在枚举常量运算后变为int型,无法自动转换为枚举spectrum型)
  • 若 int 的变量值有效(在枚举取值范围内)可强制转换为 枚举型,若无效,不会报错但后果不确定。
无名枚举作为常量

可用定义枚举但不声明枚举变量来直接使用枚举量(符号常量),并省略枚举名称。

enum {blue,orange,red,yellow};
设置枚举量的值
enum cor{blue=1,orange,red=8,yellow=12};
  • 没有初始化的符号变量(枚举量)值比前一个大 1 。
  • 可以有多个枚举量值相同。
enum cor{blue,orange=0,red,yellow=1};
枚举的取值范围
  • 上限:大于这个最大值的,最小的2的幂,将它减去1。例 最大枚举量101 的枚举上限为127。

  • 下限:最小枚举量 大于等于0则下限为0,小于0则计算方式与上限一样,只是加上负号。


2.6 指针与内存
类型修饰符与基本数据类型
  • 变量定义 = 一个数据类型 + 声明符列表

  • 声明符命名了变量并指定与给基本数据类型有关的某种类型

  • 类型修饰符就是 * ,&

  • 修饰符的个数没有限制

  • 类型修饰符是声明符的一部分

    int i = 24,*p;
    
  • 所以正确写法就是 *与名字紧贴 int *p; int & n = i;

指针危险

一定要在对指针使用解除引用运算符(*)前将指针初始化为一个确定的,适当的地址。

空指针
推荐:C++11 新标准引入 nullptr
0
cstdlib 头文件中的 NULL宏定义(也是0)
将数字赋给指针

要将数字值作为地址来使用必须通过强制类型转换将数字转换为适当的地址类型。

int * pt;
pt = (int*) 0xB8000000;
new 与 delete

new 分配的内存来自 堆(heap) 或 自由存储区
一般变量或指针来自 栈(stack)

new 分配的内存必须用 delete 释放,不要释放已释放的内存,可对空指针delete。

动态数组
创建:
int * psome = new int[10];

释放:
delete[]psome;
指针算术

对指针变量+1,其增加的值等于指向类型占用的字节数。

void * 指针
  • void* 指针可用于存放任意对象的地址
  • 用void*的指针作用有限,并不能拿它直接操作其所指的对象,仅仅就是一个内存空间地址(待详解C++PrimerP144)
指针与数组名的区别
  • 数组名是常量的指针
  • 对数组使用 sizeof 得到数组的长度,对指针使用 sizeof 得到指针的长度(大小)。

数组名被解释为第一个元素的地址(大小为数组第一个元素大小的内存块),对数组名取地址则是整个数组的地址(大小为数组的大小的内存块)。

区分 int (*psome)[10] 与 int * psome[10]

前者 int (*psome)[10] 是int数组
后者int * psome[10]是指针数组

指针与字符串

若给 cout 提供一个字符的地址(char型地址),则它从该字符开始打印直到空字符为止。

在 cout 和 多数 c++表达式中,char数组名,char地址(char指针),以及用双引号括起的字符串常量都被解释为字符串第一个字符的地址。
给 cout 提供一个指针,他将打印地址,提供 char* 指针会打印字符串,若要显示char*指针的地址可转换为int*指针。

const 与指针
(底层const)指向常量的指针
const double *cptr = &pi;
  • 禁止通过指针修改指针所指内存的值。
  • 底层const指 与本类型(指针/引用)有关的类型是const
(顶层const)常量指针
int i =42;
int *const cp = &i;
  • 常量指针必须初始化
  • 一旦初始化完成,则其地址再也不能改变
  • 可通过常量指针修改变量的值
  • 顶层const指 本类型(指针/引用)const
不存在指向引用的指针

引用本身不是对象

对指针的引用
int i=42;
int *p;
int *&r =p;
r = &i;//p=&i
*r = 0;//i=0
  • 从右往左看定义,离变量名最近的符号(&)对变量的类型有最直接的影响,所以r是个引用
面对复杂的指针/引用定义,从右往左看有助于弄清其含义
2.7 引用变量(在函数部分)
2.8 数组的替代品 vector 和 array

都是模板类
vector 长度可变
array 长度不变
都不检测中括号索引的有效性,用其 .at() 成员函数可检测索引有效性。

Vector
实例化
  • 需要包含头文件
  • 使用名称空间 std::vector
#include<vector>
using std::vector;
...
vector<int> ivec;
vector<Sales_item>Sales_vec;
vector<vector<string> > file; //该向量的元素是vector对象
//注意:外层<>与内层<>之间应该有一个空格,不然> >会与>>混淆

//引用不是对象,故引用不能包含于vector。
定义和初始化 vector对象
vector<T> v1;
vector<T> v2(v1);
vector<T> v2 = v1;//同上一个,拷贝初始化
vector<T> v3(n,val);//包含n个重复的元素,每个元素值为val
vector<T> v33{n1,n2};//v33包含两个分别初始化为n1,n2的对象
vector<T> v4(n);//v4包含n个重复地执行了值初始化的对象
vevtor<T> v44{n};//v44 包含一个值为n的对象
vector<T> v5(a,b,c,...);//
vector<T> v5 = (a,b,c,...);//同上一个

//列表初始化 vector(C++11)
vector<string> articles = {"a","an","the"};

//用数组初始化
//只需要给出指向第一个元素和最后一个元素的指针
int int_arr[] = {0,1,2,3,4};
vector<int> ivec(begin(int_arr),end(int_arr));//全部
vector<int> ivec2(int_arr+1,int_arr+3);//部分
//
vector<string> v6("hi");//Error!不是列表初始化,而是企图将字符串类型作为元素个数
向 vector 对象中添加元素
  • 成员函数 .push_back
  • 将对象作为尾元素压入 vector对象
  • 注意:若在循环体内调用了该函数,则不能使用范围for循环
  • 即,范围for语句体内不应改变其所遍历序列的大小
vector<int> v2;
for(int i=0;i!=100;++i)
	v2.push_back(i);
//v2包含从0到99
其他 vector 操作
v.empty() //vector为空则返回真;否则返回假
v.size() //返回v中元素个数
v.push_back(t) //向v的尾端添加一个值为t的元素
v[n] //返回v中第n个位置上元素的引用 n类型为 vector<T>::size_type
    //下标从0开始
v1 = v2 /拷贝v2元素拷贝替换 v1 中的元素
v1 = {a,b,c} //将列表元素拷贝替换 v1 中的元素
v1 == v2 //v1和v2值相等
<,<=,>,>= //以字典顺序进行比较
2.9 迭代器简介
使用迭代器
  • 有迭代器的类型同时拥有返回迭代器的成员
  • 这些类型都拥有名为 begin 和 end 的成员
auto b = v.begin(),e = v.end();
//begin 返回指向第一个元素的迭代器
//end 返回指向尾后迭代器(尾元素的下一个位置),只是起标记作用,并不能实际使用
//如果 v 为空,则两个函数返回同一个迭代器,都是尾后迭代器
迭代器运算符
*iter //返回迭代器iter所指元素的引用
iter->men //解引用iter并获取该元素的名为men的成员,等价于 (*iter).mem
(*iter).mem  //注意解引用要用圆括号括起来
++iter //令iter指示容器中的下一个元素
--iter //令iter指示容器中的上一个元素
iter1 == iter2 //判断迭代器是否相等
iter1 != iter2 
  • 解引用意味着迭代器必须合法且确实指示某个元素
  • 指针就是一种迭代器
迭代器类型
  • 拥有迭代器的标准库类型使用 iterator 和 const_iterator 来表示迭代器的类型
vector<int>::iterator it;//it是vector<int>类型的迭代器
string::iterator it2;//it2是string类型的迭代器

vector<int>::const_iterator it3;
string::const_iterator it4;
//只能通过该迭代器读取元素,不能修改
end 和 begin 运算符
  • 如果对象是常量则 begin和end返回 const_iterator,否则返回 iterator
  • 如果只是想要常量迭代器(const_iterator),则调用 cbegin 和 cend (C++11)
范围for循环与迭代器
  • 不能在范围for循环中向容器添加元素
  • 任何一个可能改变vector对象容量的操作,都会使容器对象的迭代器失效
  • 谨记,但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素
迭代器运算
iter + n
iter - n //返回 difference_type,带符号的,拥有迭代器类型的类型的常量
iter += n
iter -= n
iter - iter2;
>,>=,<,<=
类型别名
传统:typedef
typedef double wages;
typedef wages base,*p;//p <=> double *
别名声明 using(C++11)
using ST = String; //ST 是 String的同义词
! 指针,常量与类型别名
typedef char *pString;
//pString就是char指针,离pString最近的const表示指针是常量 
const pString cstr = 0;//cstr是指向char的常量指针
const pString *ps; //ps是指向 指向char的指针 的常量指针
!!! 不能简单替换类型别名来判断
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值