C++11 Primer Plus(一)之基础

284 阅读11分钟

自己写的C++11 Primer Plus 学习笔记,如有雷同不胜荣幸,如有错误敬请指正


1. 开始

1. main函数
建议使用 int main() 并且可选return 0; (因为编译器默认添加);
避免使用 void main() 因为它可能在一些系统上无法工作。

2. 头文件名

头文件命名约定

头文件类型 约定 事例 说明
C++旧风格 以.h结尾 iostream.h C++程序可以使用
C旧风格 以.h结尾 math.h C,C++程序可以使用
C++新式风格 没有扩展名 iostream C++程序可以使用,使用 namespace std
转换后的C 加上前缀 c,没有扩展名 cmath C++程序可以使用,可以使用不是C的特性,如:namespace std

3. 名称空间: using namespace std

4. cout输出:
1. cout<<endl(换行) 可以确保程序继续运行前刷新输出,而\n不能提供这样的确保。
2. dec, hex, oct : 分别指示 cout 以十进制,十六进制,八进制格式显示整数。
3. cout 一般会删除浮点数结尾的零,可以使用 cout.setf(ios_base::fixed,ios_base::floatfield) 覆盖此行为

5. 代码格式化:

  • 一行代码中不可分割的元素叫标记
  • 空格,制表符和回车统称空白

2. 处理数据

1. 变量名命名规则

  • 在名称中只能使用字母字符,数字和下划线
  • 名称的第一个 字符不能是数字
  • 区分大小写
  • 不能将 C++ 关键字用作名称
  • 以两个下划线或下划线和大写字母打头的名称被保留给实现(编译器及其使用的资源)使用。以一个下划线开头的名称被保留给实现,用作全局标示符。
  • C++对于名称的长度理论上没有限制 (ANSI C(C99标准)只保证名称中的前63个字符有意义)

2. 整形

  • short 至少16位
  • int 至少与 short 一样长(自然长度)
  • long 至少32位,且至少与 int 一样长
  • long long 至少64位,且至少与 long 一样长

注:
1. 自然长度:指的是计算机处理起来效率最高的长度,尽可能选用自然长度
2. 如果知道变量可能表示的整数值大于16位整数的最大可能值,则使用 long ,即使系统上 int 为32位。这样做可以确保移植的安全性。
3. 假设要将程序从 int 为16位的系统移到 int 为32位的系统,则用于存储 int 的数组的内存量将加倍,但 short 数组不受影响。
4. 整数后面的 l 或 L 后缀表示为 long 常量,u 或 U 表示 unsigned int 常量,ul 表示 unsigned long 常量,ll 或 LL 表示long long 常量,ull, Ull, uLL, ULL 表示 unsigned long long 常量。

3. 初始化方式

  • int a = 123
  • int b(234)
  • int c = {345}int c{345} => 该方法可以更好的防范类型转换错误(具体可以参考后面的类型转换)

注: 如果不对函数内部定义的变量进行初始化,那么变量的值将是不确定的,这意味着变量的值将是它被创建之前的相应的内存单元保存的值。

4. 字符

  • 通用字符名:\u 后面是8个十六进制位,\U 后面是16个十六进制位。
  • wchar_t (宽字符类型)可以表示扩展字符集,其类型是一种整数类型,可以加上前缀 L 来指示,使用 wcin, wcout 处理wchar_t流。

    wchar_t bob = L'P';
    wcout << L"tall" << endl;
  • char16_t 与 char32_t 类型都是无符号整形,前者长16位,后者长32位,分别用 uU 前缀表示。
  • 原始字符串 在原始字符串中,字符表示的就是自己,如:R"( cahr )"R"+*( "(char)" )+*"

5. const 限定符

  • 它能够明确指定类型
  • 可以使用 C++ 的作用域规则将定义限制在特定的函数或文件中(作用于规则描述了名称在各种模块中的可知程度)
  • 可以将 const 用于更复杂的类型(后续文章会多次提及)

6. 浮点数

计算机将浮点数分成两部分存储,一部分标示值,另一部分用于对值进行放大或缩小

表示方法:

  • 标准小数点表示法  如:12.34
  • E 表示法 如:2.34E23, 3.4e-2

优缺点:

  • 优点:可以表示整数之间的值,且有缩放因子,所以表示的范围更大
  • 缺点:浮点运算的速度通常比整数慢,且精度将降低,可以参考 IEEE 754-1985

7. 类型转化

专递参数时的类型转换通常由 C++ 函数原型控制。
列表初始化不允许缩窄,即变量的类型可能无法强制转换。

const int code = 66;  //有 const
int x = 66;       //无 const
char c1 {31325}; //narrowing,not allowed,31325超过一个字节表示的整数,同时 {} 不允许缩窄。注意下面的 c5
char c2 = {66};  //allowed because char can hold 66
char c3 {code};  //ditto c2
char c4 = {x};   //not allowed,x is not constant
x = 31325;
char c5 = x;     //allowed
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

整形提升(自动转换): 在计算表达式时,C++ 将 bool, char, unsigned char, signed char, short 转换为 int .

不同类型进行算术运算时(自动转换):

  • 如果有一个操作数的类型是 long double,则将另一个操作数转换为 long double
  • 否则,如果有一个操作数的类型是 double,则将另一个操作数转换为 double
  • 否则,如果有一个操作数的类型是 float,则将另一个操作数转换为 float
  • 否则,说明操作数都是整形,因此执行整形提升
  • 在这种情形下,如果两个操作数都是有符号或无符号的,且其中一个操作数的级别比另一个低,则转换为级别高的类型。
  • 如果一个操作数位有符号的,另一个操作数位无符号的,且无符号操作数的级别比有符号操作数高,则将有符号操作数转换为无符号操作数所属的类型。
  • 否则,如果有符号类型可表示无符号类型的所有可能值,则将无符号操作数转换为有符号操作数所属类型。
  • 否则,将两个操作数都转换为有符号类型的无符号版本。

强制类型转换: 强制类型转换不会修改变量本身,而是创建一个新的,制定类型的值。格式:通用->(typeName)value / typeName(value) ,更严格-> static_cast<typeName>(value)

auto: 自动类型推断,也可用于指出当前变量为局部自动变量


3. 复合类型

1. 数组

初始化数组:

  • 可省略等号

    int arr[4] = {1,2,3,4};
    int arr[4] {1,2,3,4};
  • 可不在大括号内包含任何东西

    int arr[4] = {};
    int arr[4] {};
  • 初始化时,禁止缩窄转换

2. 字符串

C-风格字符串性质:以空字符结尾,空字符被写作 \0 ,其 ASCII 码为0,用来标记字符串的结尾。

应使用 strcpy() 或 strncpy(),而不是赋值运算符来将字符串附给数组

char dog[8] = {'b','e','a','u','x',' ','I','I'};  //not a string!
char dog[8] = {'b','e','a','u','x',' ','I','\0'};  //a string!
  • 1
  • 2
char str = 'S'; //#1
char str = "S"; //#2 illegal type mismatch
  • 1
  • 2

注意: “S” 不是字符常量,它表示的是两个字符(字符S和 \0 )组成的字符串。更糟糕的是,”S” 实际上表示的是字符串所在的内存地址。

3. 字符串输入

  • cin: cin 使用空白(空格,制表符和换行符)来确定字符串的结束位置
  • cin.getline(arrName,arrSize): getline() 函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。
  • cin.get(): 其中一种方法与 getline() 相似,但是会将换行符丢弃在输入队列中。解决:cin.get(arrName,arrSize).get();
  • 空行和其他问题: 当 get() 读取空行后将设置失效位,这意味着接下来的输入将被阻断。解决:cin.clear()。如果输入行包含的字符数比指定的多,则 getline() 和 get() 将把余下的字符留在输入队列中,而 getline() 还会设置失效位,并关闭后面的输入。

4. 结构体,共用体,枚举

结构体::

C++ 不提倡使用外部变量,但提倡使用外部结构声明。

如果结构标示符是结构名,则使用句点运算符;如果标识符是指向结构的指针,则使用箭头运算符。

定义:

struct inflatable
{
    char name[20];
    float volume;
    double price;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

声明结构的不同:

struct inflatable gooes;  //C
inflatable vincent;       //C++
  • 1
  • 2

结构体中的位字段: 字段的类型应为整形或枚举,接下来是冒号,冒号后面是一个数字,它指定了使用的位数。

struct torgle_register
{
    unsigned int SN : 4;    //4 bits for SN value
    unsigned int : 4;       //4 bits unused
    bool goodIn : 1;        //valid input (1 bit)
    bool goodTorgle : 1;    //successful torgling
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

共用体::
空间: 共用体的长度为其最大成员的长度。
用途: 当数据项使用两种或更多种格式时,可节省空间。

union one4all
{
    int int_val;
    long long_val;
    double double_val;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

枚举::

enum spectrum {red,orange,yellow,green,blue,violet,indigo,ultraviolet}
  • 1
  • 让 spectrum 成为新类型的名称;spectrum被称为枚举
  • 将red,orange,yellow等作为符号常量,第一个枚举量的值位0,第二个位1,以此类推。

枚举是整形,可被提升为 int 类型,但 int 类型不能自动转换为枚举类型

也可以这样定义枚举:

enum bits{one = 1,two = 2,four = 4,eight = 8};
bits myFlag;
  • 1
  • 2

myFlag = bits(6); 是合法的,6不是枚举值,但它位于枚举定义的取值范围内。

5. 指针和自由存储空间

对每个指针变量名都需要使用一个 * : int* p1,p2 创建一个指针 p1 和一个int 变量 p2

指针的危险:在C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存。
警告:一定要在对指针应用解除引用运算符(*)之前,将指针初始化为一个确定的,适当的地址。

int *pt;
pt = (int*) 0xB8000000;
  • 1
  • 2

这样的赋值语句的两边都是整数的地址,因此这样的赋值有效。注意,pt 是 int 值的地址并不意味着 pt 本身的类型是 int。例如:有些平台,int 类型是2bit,而地址是4bit.

new运算符::

int *pn = new int;
  • 1

new int 告诉程序,需要适合存储 int 的内存,new 运算符根据类型来确定需要多少字节的内存。然后,它找到这样的内存,并返回其地址。接下来,将地址附给 pn,pn是被声明为指向 int 的指针。
变量 pn 的值被存储在被成为栈的内存区域中,而 new 从被成为堆或自由存储区的内存区域分配内存

静态联编: 在编译时给数组分配内存。
动态联编: 在程序运行时选择数组的长度,即在程序运行时为数组分配空间,其长度也将在运行时设置。这意味着数组是在程序运行时创建的,即动态数组:int * psome = new int[10];

delete运算符::

int * ps = new int;
delete ps;
int * psome = new int[10];
delete [] psome;
  • 1
  • 2
  • 3
  • 4

只能用 delete 来释放使用 new 分配的内存。然而,对空指针使用 delete 是安全的

new 与delete规则:

  • 不要使用 delete 来释放不是 new 分配的内存
  • 不要使用 delete 释放同一个内存块两次
  • 如果使用 new [] 为数组分配内存,则应该使用 delete [] 来释放
  • 如果使用 new [] 为一个实体分配内存,则应该使用 delete(无方括号) 来释放
  • 对空指针使用 delete 是安全的

指针运算:: 将指针变量加 1 后,增加的量等于它指向的类型的字节数。将指向 double 的指针加 1 后,如果系统对 double 使用8字节存储,则数值将增加8.

6. 自动存储,静态储存,动态存储

自动变量: 在函数内部定义的常规变量使用自动储存空间。
静态储存: 静态储存是在整个程序执行期间都存在的存储方式。一:在函数外面定义,二:在声明变量时使用关键字static
动态存储: 管理一个内存池,在C++ 中被称为自由存储空间,该内存池同用于静态变量和自动变量的内存是分开的。

7. at():

vector<double>a2(4) = {1.0,2.0,3.0,4.0};
  • 1

使用 at() 时,将在运行期捕获非法索引,因为 a2[-2] 是被允许的。