《c++程序设计--谭浩强》读书笔记
阅读数:285 评论数:0
跳转到新版页面分类
C/C++
正文
第一章 C++初步认识
C++对C的“增强”,表现在两个方面:
(1)在原来面向过程的机制基础上,对C语言的功能做了不少扩展
(2)增加了面向对象的机制
面向对象和面向过程不是矛盾的,而是各有用途、互为补充,不要把它们对立起来。
标准C++要求main函数必须声明为int型。有的操作系统要求执行一个程序后必须向操作系统返回一个值。因此,C++的处理是这样的:如果程序正常运行,则向操作系统返回数值0,否则返回数值-1,在目前使用的一些C++编译系统并未完全执行C++这一规定。
C++标准库中的类和函数是在命名空间std中声明的。
在C++发展的早期,为了和C语言兼容,许多C++编译系统保留头文件以.h为后缀的用法,如iostream.h,但后来ANSI C++建议头文件不带后缀.h。由于C语言无命名空间,因此用带后缀.h的头文件时也不必用“using namespace std;”作声明。一般main函数也是void类型。
编辑-》源程序-》编译-》目标程序-》连接(库文件和其他目标程序)-》可执行程序
第二章 数据类型与表达式
C++并没有统一规定各类数据的精度、数值范围、内存中所占用的字节数,因编译系统不同而不同,下面是Visual C++的情况。
类型 字节 范围 写法,[]中的为可选项
short 2 short [int]、unsigned short [int]
int 4 [signed] int, unsigned [int]
long int 4 long [int], unsigned long [int]
char 1 [signed] char, unsigned char
float 4
double 8
long double 8
bool 1
enum
数组类型
struct
union
class
指针类型 4 如果是64,就是8
引用类型
空类型
数据类型
+基本类型
+整形
-short
-int
-long
-字符型
+浮点型
-float
-double
-long double
-布尔型
+构造类型
-enum
-数组类型
-struct
-union
-class
-指针类型
-引用类型
-空类型(void)
(1)对于整数类型,C++没有规定每一种数据所占用的字节数,只规定int型数据所占的字节数不大于long型,不小于short型。
(2)整形和字符型前面可以加修饰符signed和unsigned,如果指定为signed,则数值以补码形式存放。
整数常量有不同的表示方式:
(1)后面加一个字母l或L,表示long int。这往往用于函数调用中,如果函数的形参为long int,则要求实参也为long int,此时用123作实参不行,而要用123L作实参。
(2)在常数的开头加一个数字0,就表示 是以八进制形式表示的常数
(3)在常数的开头加一个数字0和一个字母X(或x),就表示以十六进制的常数
浮点数常量的不同表示方式:
(1)十进行小数形式。由整数部分和小数部分组成,可以省略其一(如78.,.06,.0),但不能二者皆省略。一般按double类型处理,如果数字后有F或f,表示按float处理,如果后有L或l,则按long double处理。
(2)指数形式。如3.14e0,e前一定要有数字。按double处理。
‘a'的ASCII码是97,而‘A'的ASCII码是65.每个小写字母比它相应的大写字母大32。
编译系统会在字符串最后加上一个’\0'作为字符串结束标志,但‘\0’并不是字符串的一部分。
如果在一个字符串中最后一个字符为“\”,则表示它是一个续行符,下一行的字符是该字符串的一部分,且在两行字符串间无空格。如:
cout<<"We must study c\
++ hard!";
C++规定标识符只能由字母、数字、下划线组成,且第一个字符为字母或下划线。
区别const与#define
#define的符号常量只是用一个符号代替一个字符串,在预编译时把所有符号常量替换为所指定的字符串,它没有类型,在内存中并不存以符号常量命名的存储单元。而const常变量具有变量的特征,它具有类型,在内存中存在着以它命名的存储单元,可以用sizeof运算符测出其长度。与一般变量的不同是其值不能改变。虽然二者的实现方法不同,但从使用的角度看,都可以认为用了一个标识符代表了一个常量。
运算符与结合性 优先级 运算符 含义 结合方向
1 :: 域运算符 自左向右
2 ()
[ ]
->
.
++
-- 括号,函数调用
数组下标运算符
指向成员运算符
成员运算符
自增运算符(后置)
自减运算符(后置) 自左向右
3 ++
--
~
!
-
+
*
&
(类型)
sizeof
new
delete 自增运算符(前置)
自减运算符(前置)
按位取反运算符
逻辑非运算符
负号运算符
正号运算符
指针运算符
取地址运算符
类型转换运算符
长度运算符
动态分配空间运算符
释放空间运算符 自右向左,
单目运算符
4 *
/
% 乘法运算符
除法运算符
求余运算符 自左向右
5 +
- 加法运算符
减法运算符 自左向右
6 <<
>> 按位左移运算符
按位右移运算符 自左向右
7 < <= > >= 关系运算符 自左向右
8 ==
!= 等于运算符
不等于运算符 自左向右
9 & 按位与运算符 自左向右
10 ^ 按位异或运算符 自左向右
11 | 按位或运算符 自左向右
12 && 逻辑与运算符 自左向右
13 || 逻辑或运算符 自左向右
14 ?: 条件运算符 自右向左
15 = += -= *= /= %=
>>= <<= &= ^= != 赋值运算符 自右向左
16 throw 抛出异常运算符 自右向左
17 , 逗号运算符 自左向右
记忆顺口溜:醋坛酸味罐,味落跳福豆。
初等-》单目-》算术-》位移-》关系,按位运算-》逻辑-》条件-》赋值-》逗号
运算中类型的自动转换
double<----float
^
|
long
^
|
unsigned
^
|
int<----char,short
(1)横向向左的箭头表示必定的转换,即使是两个float型数据相加,也先都转换成double型
(2)纵向箭头表示不同类型时转换的方向
强制类型转换有两种方式:
(1)(类型名)(表达式),源于C语言
(2)类型名(表达式),提倡
a=(b=5)赋值表达式作为右值,等价于a=b=5,因为“=”结合性自右向左。
(a=3*5)=4*3赋值表达式作为左值,此时括号不能去掉
表达式1,表达式2
先求解表达式1,再求解表达式2,整个逗号表达式的值是表达式2的值。
例如:a=3*5,a*4
结果为60
第三章 程序设计初步
在C语言中,只有产生实际操作的才称为语句,对变量的定义不作为语句,而且要求对变量的定义必须出现在本块中所有程序语句之前,在C++中,对变量的定义被认为是一条语句,并且可以出现在函数中的作何行。
输入输出流的控制符 控制符 作用
dec 设置数值的基数为10
hex 设置数值 的基数为16
oct 设置数值 的基数为8
setfill(c) 设置填充字符c,c可以是字符常量或字符变量
setprecision(n) 设置浮点数的精度为n位。在以一般十进制小数形式输出时,n代表有效数字。
在以fixed(固定小数位数)形式和scientific(指数)形式时,n为小数位数
setw(n) 设置字段宽度为n
setiosflags(ios::fixed) 设置浮点数以固定小数位数显示
setiosflags(ios::scientific) 设置浮点数以指数形式显示
setiosflags(ios::left) 输出数据左对齐
setiosflags(ios::right) 输出 数据右对齐
setiosflags(ios::skipws) 忽略前导空格
setiosflags(ios::upppercase) 数据以十六进制输出时,字母以大写表示
setiosflags(ios::lowercase) 数据以十六进制输出时,字母以小写表示
setiosflags(ios::showpos) 输出正数时,给出“+”号
需加引用iomanip头文件 ,这些设置对其后的输出均有效,而setw只对其中一个输出项有效。
第四章 函数和预处理
在C语言中规定:如果定义函数时不指定函数类型,系统会隐含指定函数为int型,因此int max(int x,int y)可以简写为max(int x,int y),但C++取消了这一规定。
在编译时将所调用函数的代码直接嵌入到主调函数中,而不是将流程转出去,这种嵌入主调函数中的函数称为内置函数,又称为内嵌函数或内联函数。语法上只需要在函数左端加一个关键字inline即可,可以在声明函数和定义函数处任何一处,但这对于编译系统而言只是建议性的。
函数模板的一般形式:
(1)template <typename T>
通用函数定义
或
(2)template<class T>
通用函数定义
实参与形参的结合是从左至右顺序进行的,因此指定默认值的参数必须放在形参表列中的最右端。另外如果函数的定义在函数调用之前,则应在函数定义中给出默认值。否则需要在函数声明中给出默认值 。
extern用法
file1.cpp file2.cpp
extern int a,b; int a=3,b=4;
int main() ...
{
cout<<a<<","<<b<<endl;
return 0;
}
但是如果file2.cpp中变为static int a=3,b=4,那么file1.cpp就不能引用了,因为static修饰后作用域只能限于本文件。
extern也可用于函数。
#include<file>和#include"file"区别
用尖括号时,系统到系统目录中寻找要包含的文件,如果找不到,编译系统就会给出错误信息。所谓系统目录是指存放C++系统的目录,而双引号形式,默认从用户当前目录找,也可以在双此号中给出路径,如果找不到再到系统目录找。
条件编译:
(1)
#ifdef 标识符
程序段1
#else
程序段2
#endif
(2)
#if 表达式
程序段1
#else
程序段2
#endif
第五章 数组
void select_sort(int array[10],int n)
void select_sort(int array[],int n)
void select_sort(int array[5],int n)
三者是等价的,因为array[]中方括号内的数值并无实际作用,编译系统对一维数组方括号内的内容不予处理。对于二维数组
void fun(int array[10][3])
void fun(int array[][3])
void fun(int array[5][3])
三者是等价的,第二维不可省,因为数组在内存中是按行存储。
字符串处理函数
(1)strcat(char[],const char[]);
(2)strcpy(char[],const char[]);
(3)strcmp(const char[],const char[]);
(4)strlen(const char[]);
第六章 指针
*(p++)与*(++p)
前者是先取*p值,然后使p加1 。后者是先使p加1,再取*p。
*(a[i]+j), *(*(a+i)+j)与a[i][j]等价
int (*p)[4]与int *p[4]的区别
前者表示p是一个指针变量,它指向包含4个整形元素的一维数组。后者是一个指针数组。
#include <iostream>
using namespace std;
int main()
{
void swap(int *,int*);
int i=3,j=5;
swap(&i,&j);
cout<<i<<" "<<j<<endl;
return 0;
}
void swap(int *p1,int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
#include <iostream>
using namespace std;
int main()
{
void swap(int &,int&);
int i=3,j=5;
swap(i,j);
cout<<i<<" "<<j<<endl;
return 0;
}
void swap(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}
第7章 自定义数据类型
结构体的成员既可以包括数据,又可以包括函数。在C语言中,在定义结构体变量时,要在结构类型名前加上关键字struct,如
struct Student stu;
C++保留了这种方式,但也可以不添加
Student stu2;
也下面两种定义struct变量的方式:
(1)struct 结构体名
{
成员列表
}变量名表列;
(2)
struct
{
成员列表
}变量名表列;
结构体变量.成员名
(*p)。成员名
p->成员名
三者等价
共用体
union 共用体类型名
{
成员表列;
};
结构体变量所占内存长度是各成员的内存长度之和,共用体变量所占内存长度等于最长的成员的长度。
(1)在每个瞬时,只能存入其中一种,而不是同时存放几种
(2)不能在定义共用体变量时对它初始化,不能用共用体变量作为函数参数
枚举
enum 枚举类型名 {枚举常量表列;}
在c语言中,定义枚举变量要包括enum关键字。如:enum weekday workday;
而在c++中,保留了这种写法,但也可以不使用enum关键字。如:weekday workday;
(1)枚举元素作为常量,它们是有值的,按定义时的顺序对它们赋值为0,1,2……
(2)也可以在声明枚举类型时另行指定枚举元素的值
如:
enum weekday {sum=7,mon=1,tue,wed,thu,fri,sat};
这样从mon=1,以后按顺序加1 ,sat为6
还可以使用typedef声明一个新的类型来代替已有的类型。
如:
typedef struct {
int month;
int day;
int year;
}DATE;
这样,DATE就是新类型名。
第八章 类和对象
第九章 关于类和对象的进一上讨论
类的数据成员是不能在声明类时初始化,应该在cpp内初始化。
参数初始化列表:
这种方法不在函数体内对数据成员初始化,而是在函数首部。如
Box::Box(int h,int w):height(h),width(w){}
默认参数的构造函数:
(1)应该在声明构造函数时指定默认值,而不能只在定义构造函数时指定默认值。
(2)声明构造函数时,形参名可以省略。如:
Box(int=10,int=10,int=10);
常对象:
常对象必须要有初值,其所有的成员的值都不能被修改。它有两种定义形式:
(1)类名 const 对象名 [(实参表列)]
(2)const 类名 对象名 [(实参表列)]
常对象不能调用非const型的成员函数(除了由系统调用的构造函数和析构函数)。
常成员函数:
如:get_time() const;
const是函数类型的一部分,在声明函数和定义函数时都要有const关键字,它不能修改数据成员,但是声明为mutable的数据成员还是可以修改的,如:
mutable int count;
常数据成员:
是用关键字const来声明的数据成员,它只能通过构造函数的参数初始化列表对常数据成员进行初始化。
数据成员 非const成员函数 const成员函数
非const的数据成员 可以引用,也可以修改 可以引用,但不可以修改
const的数据成员 可以引用,不可以修改 可以引用,不可以修改
const对象的数据成员 不允许 可以引用,不可以修改
常指针:
类名 * const 指针变量名;
这样指针值始终保持为其初值,不能改变。但是可以改变其所指向对象的值。
指向常变量的指针:
const 类型名 * 指针变量名;
(1)如果一个变量已被声明为常变量,只能用指向常变量的指针变量指向它。
(2)当指向非const变量时,不能通过指针改变该变量的值。
静态数据成员:
(1)在所有对象之外单独开辟空间
(2)只能在类体外进行初始化,同时不能用参数列表进行初始化。
数据类型 类名::静态数据成员名=初值;
不必在初始化语句中加入static。
静态成员函数:
与静态数据成员不同,静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员。
(1)没有this指针
(2)可以直接引用本类的静态数据成员
友元:
friend关键字可以使其访问类的私用函数。友元关系不能传递,即如果B类是A类的友元,C是B类的友元,不代表C是A的友元。
类的提前声明:
类的声明的使用范围是有限的,只有在正式字义了一个类以后,才能用它定义类对象。
模板:
模板类,如:
template <class numtype>
class Compare
{...};
使用方法:
Compare<int> cmp;
如果在类模板外定义成员函数,应写成类模板形式:
template<class 虚拟类型参数>
函数类型 类模板名 <虚拟类型参数> ::成员函数名(函数形参表列) {...}
第10章 运算符重载
f运算符重载实质上是函数的重载。一般格式如下:
函数类型 operator 运算符名称 (形参列表)
{对运算符的重载处理}
(1)c++不允许用户自定义新的运算符,只能对已有的C++运算符进行重载。
(2)有5种运算符不能重载:成员访问运算符(.)、成员指针访问运算符(.*)、域运算符(::)、长度运算符(sizeof)、条件运算符(?:)
(3)不能改变操作数的个数
(4)不能改变运算符优先级
(5)不能改变运算符的结合性
(6)不能有默认的参数
(7)重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应有一个类对象(类对象引用)
C++约定,在自增(自减)运算符重载函数中,增加一个int形参,就是后置自增(自减)运算符。如
Time operator ++();//前置++
Time operator ++(int);//后置++
istream &operator >>(istream &,自定义类型 &);
ostream &operator <<(ostream &,自定义类型 &);
转换构造函数的作用是将一个其他类型转换成一个类的对象。
类型转换函数的作用是将一个类的对象转换成另一个类型的数据。
类型转换函数的一般形式:
operator 类型名()
{实现转换的语句}
第11章 继承与派生
虚基类:
C++提供虚基类的方法,使得在继承间接共同基类时只保留一份成员。虚基类并不是在声明基类时声明,而是在申明派生类时,指定继承方式时声明的。如:
class 派生类:virtual 继承方式 基类名
在最后的派生类中不仅要负责对其直接基类进行初始化,还要负责对虚基类的初始化。
第12章 多态性与虚函数
虚函数的使用方法
(1)在基类中用virtual声明成员函数为虚函数。这样就可以在派生类中重新字义此函数,为它赋予新的功能,并能方便地被调用。在类外定义虚函数时,不必再加virual。
(2)当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此在派生类重新声明该函数时,可以加virtual,也可以不加,但习惯上一般在每一层声明该函数时都加virtual,使程序更加清晰。
(3)通过虚函数与指向基类对象的指针以实现多态性。
(4)有时在基类中定义的非虚函数会在派生类中被重新定义,如果用基类指针调用该成员函数,则系统会调用对象中基类部分的成员函数,这显然不是多态性。同一类族的虚函数的首部是相同的,而函数重载时函数的首部是不同的(参数个数或类型不同)
如果将基类的析构函数函数声明为虚构函数时,由该基类所派生的所有派生类的析构函数也都自动成为虚函数。
声明纯虚函数的一般形式:
virtual 函数类型 函数名(参数列表)=0;
(1)纯虚函数没有函数体(2)最后的=0;只是告诉编译系统“这是纯虚函数”(3)这是声明语句,最后应用分号。
凡是包含纯虚函数的类都是抽象类,如果在派生类中没有对所有的纯虚函数进行定义,则此派生类仍然是抽象类,不能用来定义对象,但是可以定义指向抽象类的指针。
第13章 输入输出流
常用的头文件:
iostream 包含了对输入输出流进行操作所需的基本信息
fstream 用于用户管理的文件的IO操作
strstream 用于字符串流IO
stdiostream 用于混合使用C和C++的IO机制时,例如想将C程序转变为C++程序
iomanip 在使用格式化IO时应包含此头文件
流成员函数 与之作用相同的控制符 作用
precision(n) setprecision(n) 设置实数的精度为n位
width(n) setw(n) 设置字段宽度为n
fill(c) setfill(c) 设置填充字符c
setf() setiosflags() 设置输出格式状态,括号中应给出格式状态,内容与控制符setiosflags括号中的内容相同
unsetf() resetioflags 终止已设置的输出格式状态
流成员函数put
专用于输出单个字符的成员函数。还可以使用putchar,它是C语言中使用的,C++保留了这个函数。
流成员函数get
用于字符的输入,有3个形式
(1)cin.get()
若遇到输入流中的文件结束符,则函数返回文件结束标志EOF(End Of File)
(2)cin.get(ch)
从输入流中读取一个字符,赋值给字符变量ch,如果读取成功则函数返回非0值。
(3)cin.get(字符数组,字符个数n,终止字符)
从输入流中读取n-1个字符,赋给指定的字符数组,如果在读取n-1字符之前遇到指定的终止字符,则提前结束读取。
流成员函数getline
用于读到一行字符
cin.getline(字符数组,字符个数n,终止标志字符);
流成员函数eof
表示“文件结束”,从输入流读取数据,如果到达文件末尾(遇到文件结束符),返回非零值。
流成员函数peek
观察下一个字符,cin.peek函数返回当前字符,但指针仍停留在当前位置
流成员函数putback
cin.putback(ch);
将前面用get或getline函数从输入流中读取的字符ch返回到输入流,插入到当胶指针位置,以供后面读取。
流成员函数ignore
cin.ignore(n,终止字符)
跳过输入流中n个字符,或在遇到指定的终止字符时提前结束(此进跳过包括终止字符在内的若干字符)
文件流对象.open(磁盘文件名,输入输出方式);
磁盘文件名可以包括路径,如果缺省路径,则默认为当前目录下的文件。
与文件指针相关的流成员函数 作用
gcount 返回最后一次输入所读入的字节数
tellg 返回输入文件指针的当前位置
seekg(文件中的位置) 将输入文件中指针移到指定的位置
seekg(位移量,参照位置) 以参照位置为基础移动若干个字节
tellp 返回输出文件指针当前的位置
seekp(文件中的位置 ) 将输出文件中的指针移到指定的位置
seekp(位移量,参照位置) 以参照位置为基础移动若干个字节
参照位置可以为:
ios::beg 文件开关,这是默认值
ios::cur 指针当前的位置
ios::end 文件末尾
内存流
ostrstream::ostrstream(char *buffer,int n,int mode=ios::out);
buffer是指向字符串数组首元素的指针,n为指定的流缓存区的大小
istrstream::istrstream(char *buffer);
istrstream::istrstream(char* buffer,int n);