C++ Primer Plus学习笔记


C++ Primer Plus

第1章 预备知识

区分扩展名

  • C++程序使用.C或者.cpp作为扩展名,C程序使用.c作为扩展名

  • 对于某些UINX系统,也可使用扩展名cc和cxx

  • DOS不区分大小写

    源代码文件的扩展名
C++实现 源代码文件的扩展名
UNIX C、cc、cxx、c
GNU C++ C、cc、cxx、cpp、c++
Digital Mars cpp、cxx
Borland C++ cpp
Watcom cpp
Microsoft Visual C++ cpp、cxx、cc
Freestyle Code Warrior cp、 cpp、 cc、cxx、c++

第2章 开始学习C++

1、C++语句

  • 语句是要执行的操作
  • C与C++使用终止符(terminator)而不是分隔符
  • 终止符是一个分号(;)符,他是语句结束的标志,是语句的组成部分
有以下类型C++语句
  • 声明语句:定义函数中使用的变量的名称和类型。
  • 赋值语句:使用赋值运算符(=)给变量赋值。
  • 消息语句:将消息发送给对象,激发某种行动。
  • 函数调用:执行函数。被调用的函数执行完毕后,程序返回到函数调用语句后面的语句。
  • 函数原型:声明函数的返回类型、函数接受的参数数量和类型。
  • 返回语句:将一个值从被调用的函数那里返回到调用函数中。

2、C++注释

  • C++注释以//打头,到行尾结束
  • C++也能识别C的注释/**/
  • 尽量使用C++风格注释

3、C++源代码风格

  • 每条语句占一行
  • 每个函数都有一个开始花括号和一个结束花括号,这两个花括号各占一行。
  • 函数中的语句都相对于花括号进行缩进。
  • 与函数名称相关的圆括号周围没有空白。

4、命名约定

有以下几种

  • Myfunction( )
  • myfunction( )
  • myFunction( )
  • my_function( )
  • my_funct( )

选择取决于开发团体、使用的技术或库以及程序员个人的品位和喜好。


第3章 处理数据

1、变量名命名规则

  • 在名称中只能使用字母字符、数字和下划线(_)。
  • 名称的第一个字符不能是数字。
  • 区分大写字符与小写字符。不能将C++关键字用作名称。
  • 以两个下划线或下划线和大写字母打头的名称被保留给实现(编译器及其使用的资源)使用。以一个下划线开头的名称被保留给实现,用作全局标识符。
  • C++对于名称的长度没有限制,名称中所有的字符都有意义,但有些平台有长度限制。

2、变量长度

  • bool,

  • short(short为short int的简称)至少16位(bit),两个字节(bytes),表示范围-3276832767*,*(*即*-2^152^15-1);

  • w_char(宽字符类型),可以表示扩展字符集,是一种整数类型,长度不确定

  • char,1byte=8bit;表示范围不确定,默认情况下无符号,是否有符号有C++实现决定

    • signed char,表示范围-128~127
    char fodo;//可能有符号,可能没符号
    unsigned char bar;//无符号
    signed char snark;//有符号
    
  • C++11新增类型

    • char16_t:无符号,长16位
    • char32_t:无符号,长32位
    • C++11使用前缀u表示u表示char16_t字符串常量和字符常量,如:u'C'和u"be good";前缀U表示char32_t字符串常量和字符常量,如:U'C'和U"be good"
    • 与wchar_t一样,char16_t和 char32_t也都有底层类型——一种内置的整型,但底层类型可能随系统而已。
  • int被设置为对目标计算机而言最为“自然”的长度,自然长度(natural size)指的是计算机处理起来效率最高的长度。int至少与short一样长,4byte=32bit,所能表示范围:-21474836482147483647*;*(*即*-2^312^31-1);

  • long(long为long int的简称)至少32位,且至少与int一样长,4byte=32bit,所能表示范围:-21474836482147483647*;*(*即*-2^312^31-1);

  • long long 至少64位,且至少与long一样长。

  • float,至少32位,指数范围至少是-37到37

  • double,至少48位,且不少于float,指数范围至少是-37到37

  • long double,位80、96或128位,指数范围至少是-37到37

可以从头文件cfloat或float.h中找到系统的限制。

注:wchar_t类型是一种整数类型,它有足够的空间,可以表示系统使用的最大扩展字符集。这种类型与另一种整型(底层(underlying)类型)的长度和符号属性相同。对底层类型的选择取决于实现,因此在一个系统中,它可能是unsigned short,而在另一个系统中,则可能是int。cin和 cout将输入和输出看作是char流,因此不适于用来处理 wchar_t类型。iostream头文件的最新版本提供了作用相似的工具——wcin和 wcout,可用于处理 wchar_t流。另外,可以通过加上前缀L来指示宽字符常量和宽字符串

3、运算符sizeof

sizeof运算符返回类型或数据对象的长度(单位为字节),可对类型名或变量名使用sizeof运算符。

  • 对类型名(如 int)使用sizeof运算符时,应将名称放在括号内;
  • 对变量名(如n_short)使用该运算符,括号是可选的。
cout << "int is" << sizeof(int) << " bytes.\n";
cout << "short is " << sizeof n_short << " bytes.\n";

4、头文件limits

头文件climits 定义了符号常量来表示类型的限制。INT_MAX表示类型 int能够存储的最大值,对于Windows 7系统,为2147483 647。编译器厂商提供了climits文件,该文件指出了其编译器中的值。例如,在使用16位int 的老系统中,climits文件将INT_MAX定义为32767。下表对该文件中定义的符号常量进行了总结,其中的一些符号常量与还没有介绍过的类型相关。

climits中的符号常量
符号常量 表示
CHAR_BIT char的位数
CHAR_MAX char的最大值
CHAR_MIN char的最小值
SCHAR_MAX signed char的最大值
SCHAR_MIN signed char的最小值
UCHAR_MAX unsigned char的最大值
SHRT_MAX short的最大值
SHRT_MIN short的最小值
USHRT_MAX unsigned short的最大值
INT_MAX int的最大值
INT_MIN int的最小值
UNIT_MAX unsigned int的最大值
LONG_MAX long的最大值
LONG_MIN long的最小值
ULONG_MAX unsigned long的最大值
LLONG_MAX long long的最大值
LLONG_MIN long long 的最小值
ULLONG_MAX unsigned long long的最大值

5、C++11初始化方式

C++11使得大括号初始化器用于任何类型(可以使用等号,也可以不使用),这是一种通用的初始化语法。

int hamburgers = {24};//初始化方式一
int emus{7};//初始化方式二
int rheas = 12;//初始化方式三

6、无符号类型

  • unsigned char:所占内存大小:1byte=8bit;所能表示范围:0255*;*(02^8-1)

  • unsigned(unsigned int):所占内存大小:4byte=32bit; 能表示范围:04294967295*;*(*即*02^32-1)

  • unsigned long: 所占内存大小:4byte=32bit;所能表示范围:04294967295*;*(*即*02^32-1)

7、整型字面值

  • 十进制

    int chest = 10;
    
  • 八进制:前缀为0

    int inseam = 042;
    
  • 十六进制:前缀为0x或0X

    int waist = 0x42;
    
  • 如果要以十六进制或八进制方式显示值,则可以使用cout的一些特殊特性。控制字符dec、oct和hex分别用于指示cout以十进制、八进制和十六进制格式显示整数。

    cout << dec;
    //表示cout显示十进制数
    ......
    cout << oct;
    //表示cout显示八进制数
    ......
    cout << hex;
    //表示cout显示十六进制数
    ......
    

8、确定常量的类型

  • long:后缀为L或l,如:100L
  • unsigned int:后缀为U或u,如:100U
  • unsigned long:后缀为UL(LU,lu,ul),如100ul
  • long long:后缀为LL或ll,如100LL
  • unsigned long long:后缀为ULL(LLU,llu,ull),如100ull

接下来考察长度。在C++中,对十进制整数采用的规则,与十六进制和八进制稍微有些不同。对于不带后缀的十进制整数,将使用下面几种类型中能够存储该数的最小类型来表示: int、long或long long。在 int 为 16位、long 为 32位的计算机系统上,20000被表示为 int 类型,40000被表示为long类型,3000000000被表示为long long类型。对于不带后缀的十六进制或八进制整数,将使用下面几种类型中能够存储该数的最小类型来表示:int、unsigned int long、unsigned long、long long或unsigned long long.在将40000表示为long 的计算机系统中,十六进制数Ox9C40 ( 40000)将被表示为unsigned int。这是因为十六进制常用来表示内存地址,而内存地址是没有符号的,因此,usigned int 比 long更适合用来表示16位的地址。

9、C++转义字符

C++转义字符表
字符名称 ASCII符号 C++代码 十进制ASCII码 十六进制ASCII码
换行符 NL (LF) \n 10 0xA
水平制表符 HT \t 9 0x9
垂直制表符 VT \v 11 0xB
退格 BS \b 8 0x8
回车 CR \r 13 0xD
振铃 BEL \a 7 0x7
反斜杠 \ \\ 92 0x5C
问号 ? ? 63 0x3F
单引号 ' \' 39 0x27
双引号 " \" 34 0x22

10、const限定符

创建常量的通用格式

const type name = value;//在C++中尽量用const取代#define

常量指针:const修饰指针,指针指向可以变,指针指向的值不可以变

const int * p = &a;

指针常量:const修饰的是常量,指针指向不可以变,指针指向的值可以变

int * const p = &a;

const即修饰指针又修饰常量:指针指向不可以变,指针指向的值也不可以变

const int * const p = &a;

11、浮点数的表示

12.34//float
2.52e+8//E的表示法,+可省略,E大小写皆可
-18.32e-13//E的表示法,-不可省略

1)定点表示法

ostream方法 setf(),迫使输出使用定点表示法,以便更好地了解精度,它防止程序把较大的值切换为E表示法,并使程序显示到小数点后6位。参数 ios_base:.fixed 和ios_base::floatfield是通过包含iostream来提供的常量。

#include
int main ()
{
    using namespace std;
    cout.setf(ios_base::fixed, ios_base::floatfield);
    //迫使cout输出使用定点表示法,并使程序显示到小数点后6位
    ......
    return 0 ;
}

2)浮点常量

  • float:后缀为f或F,如:1.2f
  • double:默认浮点数都是double,如:8,24和2.4E8
  • long double:后缀为l或L,如1.234L

12、除法分支

  • 整数/整数=整数(小数部分省略)

  • double/double = double

  • double/int = double

  • int/double = double

  • double/double = double

  • float/float = float

13、求模运算

求模符号:%

规则:只能为整数,x(x>=n)与n求模,求模后取值为小于n的整数,如:

//与二求模
	for(int i=1;i<=10;i++){
		//TODO
		cout << i << "%2 = " << i%2 << "  ";//与2求模,求模后取值0,1,为小于2的整数
	}
	cout << endl;
	
	//与三求模
	for(int i=1;i<=10;i++){
		//TODO
		cout << i << "%3 = " << i%3 << "  ";//与3求模,求模后取值0,1,2,为小于3的整数
	}
	cout << endl;

14、类型转换

转换规则

C++11对这个校验表稍做了修改,下面是C++11版本的校验表,编译器将依次查阅该列表:

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

有符号整型按级别从高到低依次为long long、long、int、short和 signed char。无符号整型的排列顺序与有符号整型相同。类型char、signed char和unsigned char的级别相同。类型bool 的级别最低。wchar_t、char16_t和char32_t 的级别与其底层类型相同。

强制类型转换

(typeName) value;//方法一,C语言版
typeName (value);//方法二,C++版,新格式的想法是,要让强制类型转换就像是函数调用一样
//C++还引入了4个强制类型转换运算符,对它们的使用要求更为严格
statistic_cast (value)

15、C++11中的auto声明

C++11新增了一个工具,让编译器能够根据初始值的类型推断变量的类型。为此,它重新定义了auto的含义。auto是一个C语言关键字,但很少使用。在初始化声明中,如果使用关键字auto,而不指定变量的类型,编译器将把变量的类型设置成与初始值相同:

auto n = 100;//n为int
auto x = 1.5;//x为double
auto y = 1.3e12L;//y为long double

显式地声明类型时,将变量初始化0(而不是0.0)不会导致任何问题,但采用自动类型推断时,这却会导致问题。


第4章 复合类型

1、数组

数组之所以被称为复合类型,是因为它是使用其他类型来创建的(C语言使用术语“派生类型”,但由于C++对类关系使用术语“派生”,所以它必须创建一个新术语)。

//声明数组
typeName arrayName[arraySize];

表达式arraySize 指定元素数目,它必须是整型常数(如10)或const值,也可以是常量表达式(如8*sizeof ( int)),即其中所有的值在编译时都是已知的。具体地说,arraySize不能是变量,变量的值是在程序运行时设置的。然而,本章稍后将介绍如何使用new运算符来避开这种限制。

float loans[20];

loans 的类型不是“数组”,而是“float数组”。这强调了loans数组是使用float类型创建的。

2、字符串

  • 字符串是存储在内存的连续字节中的一系列字符。C++处理字符串的方式有两种。第一种来自C语言,常被称为C-风格字符串(C-style string)。

  • C-风格字符串具有一种特殊的性质:以空字符(null character)结尾,空字符被写作\0,其ASCII码为0,用来标记字符串的结尾。例如,请看下面两个声明:

    char dog[8] = {'b', 'e', 'a', "u ', 'x', ' ', 'I','I'};//不是字符串
    char cat[8] = {'f', 'a' , 't', 'e', 's', 's', 'a', '\0'};//是字符串
    
  • 字符串常量(string constant)或字符串字面值(string literal)

    • 在cat 数组示例中,将数组初始化为字符串的工作看上去冗长乏味——使用大量单引号,且必须记住加上空字符。不必担心,有一种更好的、将字符数组初始化为字符串的方法—只需使用一个用引号括起的字符串即可。

      char bird[11] = "Mr. Cheeps";
      char fish[] = "Bubbles";
      
    • C++输入工具通过键盘输入,将字符串读入到char数组中时,将自动加上结尾的空字符

1)拼接字符串

cout <<"1'd give my right ar"
    "m to be a great violinist.\n" ;

注意,拼接时不会在被连接的字符串之间添加空格,第二个字符串的第一个字符将紧跟在第一个字符串的最后一个字符(不考虑\0)后面。第一个字符串中的\0字符将被第二个字符串的第一个字符取代。

2)strlen函数

strlen()函数返回的是存储在数组中的字符串的长度,而不是数组本身的长度。另外,strlen()只计算可见的字符,而不把空字符计算在内。

3)cin读取字符串

cin使用空白(空格、制表符和换行符)来确定字符串的结束位置,这意味着cin在获取字符数组输入时只读取一个单词。读取该单词后, cin将该字符串放到数组中,并自动在结尾添加空字符。

4)每次读取一行字符串输入

  • 面向行的输入:getline()

    getline()函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。要调用这种方法,可以使用cin.getline( )。该函数有两个参数。第一个参数是用来存储输入行的数组的名称第二个参数是要读取的字符数。如果这个参数为20,则函数最多读取19个字符,余下的空间用于存储自动在结尾处添加的空字符。getline()成员函数在读取指定数目的字符遇到换行符时停止读取。

  • 面向行的输入:get()(推荐使用)

    istream类有另一个名为 get( )的成员函数,该函数有几种变体。其中一种变体的工作方式与getline( )类似,它们接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但get并不再读取并丢弃换行符,而是将其留在输入队列中

    get()有另一种变体。使用不带任何参数的cin.get()调用可读取下一个字符(即使是换行符)因此可以用它来处理换行符,为读取下一行输入做好准备。

3、string类

在很多方面,使用string 对象的方式与使用字符数组相同。

  • 可以使用C-风格字符串来初始化string对象。
  • 可以使用cin来将键盘输入存储到string 对象中。
  • 可以使用cout来显示string 对象。
  • 可以使用数组表示法来访问存储在string对象中的字符。

C++11也允许将列表初始化用于C-风格字符串和 string对象:

char first_date[] ={"Le chapon Dodu"};
char second_date[] {"The Elegant Plate"};
string third_date = {"The Bread Bowl "};
string fourth_date {"Hank 's Fine Eats"};

string 类简化了字符串合并操作。可以使用运算符+将两个string对象合并起来,还可以使用运算符+=将字符串附加到string 对象的末尾。

string str3;
str3 = str1 + str2;
str1 += str2;

strcpy()与strcat()方法

在C++新增string类之前。对于C-风格字符串,使用C语言库中的函数来完成这些任务。头文件cstring (以前为string.h)提供了这些函数。例如,可以使用函数strcpy()将字符串复制到字符数组中,使用函数 strcat()将字符串附加到字符数组末尾:

strcpy(charr1, charr2);//复制charr2到charr1
strcat(charr1, charr2);//将charr2拼接到charr1后面

string 类具有自动调整大小的功能,从而能够避免这种问题发生,C函数库提供了与strcat()和 strcpy()类似的函数——strncat()strncpy(),它们接受指出目标数组最大允许长度的第三个参数,因此更为安全,但使用它们进一步增加了编写程序的复杂度。

size()类方法

函数strlen()是一个常规函数,它接受一个C-风格字符串作为参数,并返回该字符串包含的字符数。函数 size()的功能基本上与此相同。但size()是类方法,通过对象.size()调用。

原始(raw)字符串

在原始字符串中,字符表示的就是自己,例如,序列\n不表示换行符,而表示两个常规字符—斜杠和n,因此在屏幕上显示时,将显示这两个字符。原始字符串将"(和)"用作定界符,并使用前缀R来标识原始字符串:

cout << R"(Jim "King" Tutt uses "\n" instead of endl.)" << '\n';
//上述代码将显示如下内容:
//Jim "King" Tutt uses "\n" instead of endl .

输入原始字符串时,按回车键不仅会移到下一行,还将在原始字符串中添加回车字符。

如果要在原始字符串中包含)",使用R"+*(标识原始字符串的开头时,必须使用)+*"标识原始字符串的结尾。这使用"+*(和)+*"替代了默认定界符"(和)"。自定义定界符时,在默认定界符之间添加任意数量的基本字符,但空格、左括号、右括号、斜杠和控制字符(如制表符和换行符)除外。

cout << R"+*("( Who wouldn't?) ", she whispered.)+*" << endl ;
//" ( who wouldn't?) ", she whispered.

4、共用体

共用体(union)是一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型。也就是说,结构可以同时存储int、long 和 double,共用体只能存储int、long或double。共用体的句法与结构相似,但含义不同。

union one4all
{
    int int_val;
    long long_val;
    double double_val;
};

one4all pail;
pail.int_val = 15;
cout << pail.int_val;
pail. double_val = 1.38;
cout << pail.double_val;

pail 有时可以是int 变量,而有时又可以是double变量。成员名称标识了变量的容量。由于共用体每次只能存储一个值,因此它必须有足够的空间来存储最大的成员,所以,共用体的长度为其最大成员的长度
匿名共用体(anonymous union)没有名称,其成员将成为位于相同地址处的变量。显然,每次只有一个成员是当前的成员:

struct widget{
	char brand [20];
    int type;
	union
{
    long id_num;
	char id_char [20];
    };
};
...
widget prize;
...
if (prize.type == 1)
	cin >> prize.id_num;
else
	cin >> prize.id_char;

由于共用体是匿名的,因此id_num和 id_char被视为prize的两个成员,它们的地址相同,所以不需要中间标识符id_val。程序员负责确定当前哪个成员是活动的。

5、枚举

1)定义

使用enum的句法与使用结构相似。

enum spectrum {red,orange, yellow, green, blue,violet, indigo, ultraviolet
};

这条语句完成两项工作。

  • 让 spectrum成为新类型的名称;spectrum被称为枚举(enumeration),就像struct变量被称为结构一样。
  • 将red、 orange、yellow等作为符号常量,它们对应整数值0~7。这些常量叫作枚举量( enumerator )。

在默认情况下,将整数值赋给枚举量,第一个枚举量的值为0,第二个枚举量的值为1,依次类推。
可以用枚举名来声明这种类型的变量:

spectrum band;

在不进行强制类型转换的情况下,只能将定义枚举时使用的枚举量赋给这种枚举的变量:

band = blue;//允许
band = 2000;//非法

因此,spectrum 变量受到限制,只有8个可能的值。如果试图将一个非法值赋给它,则有些编译器将出现编译器错误,而另一些则发出警告。为获得最大限度的可移植性,应将把非enum值赋给enum变量视为错误
对于枚举,只定义了赋值运算符。具体地说,没有为枚举定义算术运算:

band = orange;//允许
++band;//不允许
band = orange + red;//不合理
...

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

int color = blue;//允许
band = 3;//不允许,int不能转换为枚举型
color = 3 + red;//允许,枚举量为整型被提升为int型
...

2)设置枚举的值

可以使用赋值运算符来显式地设置枚举量的值,指定的值必须是整数。也可以只显式地定义其中一些枚举量的值。

enum bigstep{first, second = 100, third};

这里,first 在默认情况下为0。后面没有被初始化的枚举量的值将比其前面的枚举量大1。因此,third的值为101。

可以创建多个值相同的枚举量:

enum {zero, null = 0,one,numero_uno = 1};
//其中,zero和 null都为0,one和 umero_uno都为1。

3)枚举的取值范围

每个枚举都有取值范围(range),通过强制类型转换,可以将取值范围中的任何整数值赋给枚举变量,即使这个值不是枚举值。

enum bits{ one = 1, two = 2, four = 4, eight = B };
bits myflag;
myflag = bits(6);//允许,其中6不是枚举值,但它位于枚举定义的取值范围内。

上限:需要知道枚举量的最大值。找到大于这个最大值的、最小的2的幂,将它减去1,得到的便是取值范围的上限。例如,前面定义的 bigstep 的最大值枚举值是101。在2的幂中,比这个数大的最小值为128,因此取值范围的上限为127

下限:。要计算下限,需要知道枚举量的最小值。如果它不小于0,则取值范围的下限为0;否则,采用与寻找上限方式相同的方式,但加上负号。例如,如果最小的枚举量为-6,而比它小的、最大的2的幂是-8(加上负号),因此下限为-7。

6、指针

在C++中,type*是一种复合类型,是指向type的指针。

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

1)使用new来分配内存

在C语言中,可以用库函数malloc()来分配内存;在C++中仍然可以这样做,但C++还有更好的方法—new运算符。

typeName * pointer_name = new typeName;
//在运行阶段为一个int值分配未命名的内存,并使用指针来访问这个值。程序员要告诉new,需要为哪种数据类型分配内存;new将找到一个长度正确的内存块,并返回该内存块的地址。程序员的责任是将该地址赋给一个指针。
int * pn = new int;

//在下面这种情况下,可以通过名称higgens 来访问该int,在第一种情况下,则只能通过该指针进行访问。这引出了一个问题: pn指向的内存没有名称,如何称呼它呢﹖我们说pn 指向一个数据对象,这里的“对象”不是“面向对象编程”中的对象,而是一样“东西”。术语“数据对象”比“变量”更通用,它指的是为数据项分配的内存块。因此,变量也是数据对象,但pn 指向的内存不是变量。乍一看,处理数据对象的指针方法可能不太好用,但它使程序在管理内存方面有更大的控制权。
int higgens;
int * pt = &higgens;

对于指针,需要指出的另一点是, new分配的内存块通常与常规变量声明分配的内存块不同.变量nights和pd的值都存储在被称为栈( stack)的内存区域中而new从被称为堆(heap)或自由存储区( free store)的内存区域分配内存

2)使用delete释放内存

使用delete时,后面要加上指向内存块的指针(这些内存块最初是用new分配的);只能用delete来释放使用new分配的内存。对空指针使用delete是安全的。

int * ps = new int;
...
delete ps;

3)使用new创建动态数组

int * psome = new int [10];//要创建一个包含10个int元素的数组
delete [] psome;//方括号告诉程序,应释放整个数组,而不仅仅是指针指向的元素。请注意delete和指针之间的方括号。

总之,使用new和 delete时,应遵守以下规则:

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

7、数组的替代品

1)模板类vector

模板类vector类似于string类,也是一种动态数组。可以在运行阶段设置vector对象的长度,可在末尾附加新数据,还可在中间插入新数据。基本上,它是使用new创建动态数组的替代品。实际上,vector类确实使用new和 delete来管理内存,但这种工作是自动完成的。

  • 首先,要使用vector对象,必须包含头文件 vector。

  • 其次,vector包含在名称空间std 中,因此可使用using 编译指令、using声明或std::vector。

  • 第三,模板使用不同的语法来指出它存储的数据类型。

  • 第四,vector类使用不同的语法来指定元素数。下面是一些示例:

    #include 
    ...
    using namespace std;
    vector vi;
    int n;
    cin >> n;
    vector vd(n);
    

    其中,vi是一个vector对象,vd是一个vector对象。由于vector对象在您插入或添加值时自动调整长度,因此可以将vi的初始长度设置为零。但要调整长度,需要使用vector包中的各种方法。
    一般而言,下面的声明创建一个名为vt的vector对象,它可存储n_elem个类型为typeName的元素:

    vector vt(n_elem) ;
    

    其中参数n_elem可以是整型常量,也可以是整型变量。

2)模板类array(C++11)

vector类的功能比数组强大,但付出的代价是效率稍低。如果您需要的是长度固定的数组,使用数组是更佳的选择,但代价是不那么方便和安全。有鉴于此,C++11新增了模板类array,它也位于名称空间std中。与数组一样,array对象的长度也是固定的,也使用栈(静态内存分配),而不是自由存储区,因此其效率与数组相同,但更方便,更安全。要创建array对象,需要包含头文件array。array对象的创建语法与vector稍有不同:

#include 
...
using namespace std;
array ai;
array ad = {1.2, 2.1, 3.43, 4.3};

推而广之,下面的声明创建一个名为arr 的array对象,它包含n_elem个类型为typename的元素;

array arr;

与创建vector对象不同的是,n_elem不能是变量
在C++11中,可将列表初始化用于vector和array对象,但在C++98中,不能对vector对象这样做。

3)比较数组、vector对象和array对象

  • 首先,无论是数组、vector对象还是array对象,都可使用标准数组表示法来访问各个元素。
  • 其次,从地址可知,array对象和数组存储在相同的内存区域(即栈)中而 vector对象存储在另一个区域(自由存储区或堆)中
  • 第三,注意到可以将一个array对象赋给另一个array对象;而对于数组,必须逐元素复制数据。
a2 [-2] = .5;//依然被允许
a3 [200] = 1.4 ;

可以选择是使用成员函数 at()。就像可以使用cin对象的成员函数getline()一样,您也可以使用vector和 array对象的成员函数at():

a2.at(1) = 2.3;//将2.3分配给a2[1]

中括号表示法和成员函数at()的差别在于,使用at()时,将在运行期间捕获非法索引,而程序默认将中断。这种额外检查的代价是运行时间更长,这就是C++让允许您使用任何一种表示法的原因所在。另外,这些类还让您能够降低意外超界错误的概率。例如,它们包含成员函数 begin()和 end(),让您能够确定边界,以免无意间超界。


第5章 循环和关系表达式

1、for循环

for (initialization; test-expression; update-expression)
    body

循环只执行一次初始化。通常,程序使用该表达式将变量设置为起始值,然后用该变量计算循环周期。
test-expression(测试表达式)决定循环体是否被执行。通常,这个表达式是关系表达式,即对两个值进行比较。实际上,C++并没有将test-expression 的值限制为只能为真或假。可以使用任意表达式,C++将把结果强制转换为bool类型。因此,值为0的表达式将被转换为bool值false,导致循环结束。如果表达式的值为非零,则被强制转换为bool值 true,循环将继续进行。

C++常用的方式是,在for和括号之间加上一个空格,而省略函数名与括号之间的空格。如:

for (i = 6; i < 10; i++)
    smart_function(i);

2、strcmp()函数

可以使用strcmp()来测试C-风格字符串是否相等(排列顺序)。如果 strl和str2相等,则下面的表达式为true:

strcmp(str1,str2) == 0;

3、while循环

while (test-condition)
    body

在无法预先知道循环将执行的次数时,程序员常使用while循环。
在设计循环时,请记住下面几条指导原则:

  • 指定循环终止的条件。

  • 在首次测试之前初始化条件。

  • 在条件被再次测试之前更新条件。

延时循环,clock()函数

ANSI C和C++库中有一个函数有助于完成延时工作。这个函数名为clock( ),返回程序开始执行后所用的系统时间。这有两个复杂的问题:首先,clock( )返回时间的单位不一定是秒;其次,该函数的返回类型在某些系统上可能是 long,在另一些系统上可能是unsigned long 或其他类型。但头文件ctime(较早的实现中为 time.h)提供了这些问题的解决方案。首先,它定义了一个符号常量——CLOCKS_PER_SEC,该常量等于每秒钟包含的系统时间单位数。因此,将系统时间除以这个值,可以得到秒数。或者将秒数乘以CLOCK_PER_SEC,可以得到以系统时间单位为单位的时间。其次,ctime将clock_t 作为clock()返回类型的别名,这意味着可以将变量声明为clock _t类型,编译器将把它转换为 long、unsigned int或适合系统的其他类型。

类型别名

C++为类型建立别名的方式有两种。

  • 一种是使用预处理器:#define BYTE char
    这样,预处理器将在编译程序时用char替换所有的BYTE,从而使BYTE成为char 的别名。

  • 第二种方法是使用C++(和C)的关键字typedef来创建别名。例如,要将byte作为char的别名,可以这样做:
    typedef char byte;

    下面是通用格式:
    typedef typeName aliasName;
    它能够处理更复杂的类型别名,这使得与使用#define相比,使用typedef是一种更佳的选择——有时候,这也是唯一的选择。

    注意,typedef 不会创建新类型,而只是为已有的类型建立一个新名称。如果将word作为int的别名,则cout 将把word类型的值视为int类型。

4、do while循环

do
    body
while(test-expression);

5、基于范围的for循环

C++11新增了一种循环:基于范围(range-based)的 for循环。这简化了一种常见的循环任务:对数组(或容器类,如vector和 array)的每个元素执行相同的操作,如下例所示:

double prices[5] = {4.99,10.99,6.87,7.99,8.49};
for (double x : prices)
	cout << x << std::endl;

其中,x最初表示数组prices 的第一个元素。显示第一个元素后,不断执行循环,而x依次表示数组的其他元素。因此,上述代码显示全部5个元素,每个元素占据一行。总之,该循环显示数组中的每个值。
要修改数组的元素,需要使用不同的循环变量语法:

for (double &x : prices)
	x = x * 0.80;

符号&表明x是一个引用变量,这种声明让接下来的代码能够修改数组的内容,而第一种语法不能。
还可结合使用基于范围的 for循环和初始化列表:

for (int x : {3,5, 2, 8, 6})
	cout c< x << " ";
cout c< '\n';

6、循环和文本输入

知道循环的工作原理后,来看一看循环完成的一项最常见、最重要的任务:逐字符地读取来自文件或键盘的文本。

1)使用原始的cin进行输入

如果程序要使用循环来读取来自键盘的文本输入,则必须有办法知道何时停止读取。一种方法是选择某个特殊字符——有时被称为哨兵字符( sentinel character),将其作为停止标记。

#include int main()
{
	using namespace std;
	char ch;
	int count = 0;
	cout c< "Enter characters; enter # to quit : \n";
    cin >> ch;
	while (ch !='#')
    {
	cout c< ch;
	++count;
	cin >> ch;
    }
	cout << endl cc count c< " characters read'in";
    return 0 ;
}

运行情况:

Enter characters; enter # to quit :

see ken run#really fast
seekenrun
9 characters read

为什么程序在输出时省略了空格呢?原因在 cin。读取 char值时,与读取其他基本类型一样,cin将忽略空格和换行符。因此输入中的空格没有被回显,也没有被包括在计数内。
更为复杂的是,发送给 cin 的输入被缓冲。这意味着只有在用户按下回车键后,他输入的内容才会被发送给程序。这就是在运行该程序时,可以在#后面输入字符的原因。按下回车键后,整个字符序列将被发送给程序,但程序在遇到#字符后将结束对输入的处理。

2)使用cin.get(char)进行补救

3)使用哪一个cin.get( )

4)文件尾条件

1、EOF结束输入
2、常见的字符输入做法

5)另一个cin.get版本