C++程序设计重点总结(谭浩强版)

文章目录

  • 第1章 C++初步知识
    • 1.流程
  • 第3章 程序设计
    • 1.优先级
    • 2.switch
    • break continue
  • 第4章 函数
    • 1.内置函数
    • 2.函数重载
    • 3.函数模板
    • 4.带默认参数的函数
    • 5.动态存储和静态存储
    • 6.auto(自动变量、动态存储)
    • 7.static(静态局部变量、静态存储)
    • 8.register(寄存器变量、在内存)
    • 9.extern(外部变量、静态存储、别的文件可引用)
    • 10.static(静态外部变量、静态存储、只限本文件中使用)
    • 11.内部函数外部函数
  • 第5章 数组
    • 1.一维数组
    • 2.二维数组
    • 3.字符数组
    • 4.字符串处理函数
    • 5.string
    • 6.string []
  • 第6章 指针和引用
    • 1.指针 和 指针变量
    • 2.`&` `*`
    • 3.指针作为形参
    • 4.指向数组
    • 5.指针变量 和 指针函数
    • 6.指针数组
    • 7.const指针
    • * void指针
    • 8.指针小结
    • 9.引用(本质是指针常量)
    • 10.参数传递
  • 第7章 用户自定义数据类型
    • 1.结构体类型(`struct`)
    • 2.链表
    • 3.`new`和`delete`
    • 4.枚举类型(枚举常量)
    • 5.typedef声明新类型名
  • 第9章 类和对象
    • 1.构造函数
    • 2.析构函数
    • 3.构造函数、析构函数执行顺序
    • 4.对象指针
    • 5.this指针
    • 6.共享数据的保护
    • 7.指向对象的常指针
    • 8.指向常对象的指针变量
    • 9.对象的常引用
    • 10.对象的动态建立和释放
    • 11.1 对象赋值
    • 11.2 对象复制
    • 13.静态成员(static)
    • 14.友元
      • 友元函数
      • 友元类
    • 15.类模板
  • 第10章 运算符重载(不在考纲内)
    • 1.运算符重载规则
    • 2.运算符重载函数作为类成员函数
    • 3.运算符重载函数作为友元函数
    • 4.单目运算符重载
    • 5.重载流运算符(必须是友元函数)
    • 6.转换构造函数
    • 7.类型转换函数
  • 第11章 继承与派生
    • 1.派生类构成
    • 2.权限
    • 3. 派生类的构造函数
    • 4. 派生类的析构函数
    • 5.多重继承
    • 6.虚基类
    • 7.基类和派生类的转换
    • 8.小结
  • 第12章 多态性和虚函数
    • 1.静态多态和动态多态
    • 2.虚函数(=基类声明虚函数+基类对象指针调用派生类方法)
    • 3.虚析构函数
    • 4.纯虚函数
    • 5.抽象类
    • 6.静态关联和动态关联
  • 第13章 输入输出流
    • 1.C++流库
    • 2.标准输出流(ostream类的3个对象)
    • 3.格式控制
    • 4.标准输入流(cin)
    • 5.字符输入的成员函数
    • 6.istream类的其他成员函数
    • 7.文件
    • 8.文件流类和文件流对象
    • 9.对ASCII码文件的操作
    • 10.对二进制文件的操作
    • 11.文件指针有关函数

第1章 C++初步知识

1.流程

编辑 -> 编译(.cpp) -> 连接(.obj) -> 运行(.exe) -> 分析结果

第3章 程序设计

1.优先级

! > 算术运算符 > 关系运算符 > &&和|| > 赋值运算符

2.switch

switch(表达式)
{
    case 常量表达式1: 语句1; break;
    case 常量表达式2: 语句2; break;
    ...
    default: 语句; break;
}

break continue

while(表达式1)
{
    if()
        continue;   //结束本次循环,继续下次循环
    if()
        break;  //结束全部循环
}
  • 表达式可以是数值类型包括字符类型数据
  • 次序不影响结果
  • case表达式的值必须互不相同

第4章 函数

C++在程序进行编译时,以程序文件模块为编译单位

1.内置函数

编译时将调用函数的代码直接嵌入到主调函数中,而不是将流程转出去

inline int max(int, int, int);

inline int max(int a, int b, int c)
{
    ...
}
  • 规模小
  • 使用频繁
  • 不包含复杂控制语句,如循环语句和switch语句
  • 建议性,而非指令性

2.函数重载

重载函数的参数格式、参数类型、参数顺序三者中必须至少有一种不同,返回值类型可同可不同

3.函数模板

函数模板只适用于函数体相同、函数的参数相同而类型不同的情况
如果参数的个数不同,则不能用函数模板。

template 
T max(T a, T b, T c)
{
    ...
}

4.带默认参数的函数

  • 实参和形参的结合是从左至右,指定默认值的参数必须放在形参列表的最右边
  • 声明中给出默认值,定义时可以省略
  • 一个函数不能既作为重载函数,又作为有默认参数的函数

5.动态存储和静态存储

  • 动态存储:动态分配空间
  • 静态存储:分配固定空间

6.auto(自动变量、动态存储)

int f(int a)
{
    auto int b, c = 3;  //自动变量,默认为auto,可省略
}
  • f函数被调用结束后,变量被销毁
  • 不赋初值,则它的值是个不确定的值

7.static(静态局部变量、静态存储)

int f(int a)
{
    static int b = 0;   //静态局部变量
}
  • f函数被调用结束后,变量保留,外边不可以调用
  • 不赋初值,编译时自动赋值0或空字符

8.register(寄存器变量、在内存)

int f(int a)
{
    register int i=1;   //寄存器变量
}
  • 使用频繁的可以声明为寄存器变量
  • 建议性,而非强制性

9.extern(外部变量、静态存储、别的文件可引用)

1.在一个文件内声明全局变量

int main()
{
    extern int a, b;    //全局变量的提前引用声明
    cout<

2.在多文件的程序中声明外部变量

file1.cpp

int a=3, b=4;
...

file2.cpp

extern int a, b;    //在编译连接成一个程序后,将file1.cpp中的a、b作用域扩展到file2.cpp

int main()
{
    cout<

10.static(静态外部变量、静态存储、只限本文件中使用)

file1.cpp

static int a = 3;   //静态外部变量,只限于本文件中用

file2.cpp

extern int a;   //引用不到file2.cpp中的a,因为是静态外部变量
int main()
{
    cout<

11.内部函数外部函数

  • 内部函数:函数只能被本文件中的其他函数调用
static int func(int a, int b)
{
    ...
}
  • 外部函数:可供其他函数调用,默认都是外部函数
    file1.cpp
int max(int x, int y)
{
    ...
}

file2.cpp

int main()
{
    extern int max(int, int);   //声明要调用其他文件中的max函数
    max(1, 2);   
}

第5章 数组

  • 字符串结束标志符\0(ASCII码为0)

1.一维数组

int a[10];  //不允许对数组的大小作动态定义

const int n = 5;
int a[n];   //合法,因为n是常量

2.二维数组

int a[3][4];        
int a[][4] = {1, 2, 3, 4, 5, 6}//第2维的长度不能省略

3.字符数组

char a[10] = {'I', 'a', 'm', 'h', 'a', 'p', 'p', 'y'};
char c[] = "I am happy";    //正确
c = a;  //错误
c = {'I', 'a', 'm', 'h', 'a', 'p', 'p', 'y'};   //错误
c[0] = 'I'; //正确

char str[20];
cin>>str;   //输入字符串
cout<

4.字符串处理函数

#include 
或
#include 
  • strcat:字符串连接函数
strcat()
  • strcpy:字符串复制函数(覆盖)
char str1[10], str2[] = "China";
strcpy(str1, str2); //第1个实参必须是数组名,第2个实参可以是数组名,也可以是字符串常量
strcpy(str1, "China");

strcpy(str1, str2, 2);  //将str2中前面2个字符复制到str1中去,然后加上'\0'
  • strcmp:字符串比较函数
strcmp(str1, str2); //正确
strcmp("China", "Korea");   //正确
strcmp(str1, "Beijing");    //正确

串1 == 串2,返回0

串1 > 串2,返回正数

串1 < 串2,返回负数

  • strlen:字符串长度函数
char str[10] = "China";
cout<

5.string

string不是C++本身的基本类型,而是C++标准库中声明的一个字符串类

  • 需要引入头文件
#include 
  • 定义
string s1, s2;
s1 = "Hello";
s2[0] = 'a';    //合法修改第一个字符
  • 输入输出
cin>>s1;
cout<
  • 运算
string s1, s2;
s1 = "Hello";
s2 = s1;    //赋值运算
s2 = s1 + s2;   //连接
if(s1 > s2) ...;    //比较运算符   

6.string []

string name[3] = {"Zhang", "Li", "Sun"};

第6章 指针和引用

1.指针 和 指针变量

  • 变量的指针:变量的地址
  • 指针变量:一个变量专门存放地址(指针)

2.& *

自右向左结合

int a = 10;
int *pointer_1, *pointer_2;
pointer_1=&a;
pointer_2 = &*pointer_1;    //从右向左执行,将a的地址赋给pointer_2

3.指针作为形参

调用函数时不会改变实参指针变量的值(地址),但可改变实参指针变量所指向的值

int a = 10
int *p = a;
fun(p);
int fun(int *p){}   //不改变p的值,但可改变a的值

4.指向数组

启示指向的是数组的第一个元素

int a[10];
int *p;

p=&a[0];    //等价于  
p=a;

p+1 //表示数组的下一个元素

void select_sort(int array[], int n)    //等价于 
void select_sort(int *array, int n)

5.指针变量 和 指针函数

  • 指针变量(2个括号)【是变量】
int a=1, b=1;
int max(int x, int y);

int (*p)(int, int); //  是变量,可以指向函数
p=max
p(a, b) //用函数指针调用 等价于
max(a, b)
  • 指针函数(1个括号)【是函数】
int *p(int x, int y) {} // 是函数,返回值为int型指针

6.指针数组

int *p[4];  //每个元素都是指针类型
相当于:
int *p0; int *p1; int *p2; int *p3;

例:
char *name[] = {"peter", "jack", "smith"};  //指针数组

[]优先级比*
注意:

int (*p)[4];   //指向一维数组的指针变量 用来指向二维数组
#include
using namespace std;

int main()
{
   int w[3][4] = {{1,2,3, 11}, {4,5,6, 12}, {7,8,9, 13}};
   int (*p)[4] = w;    //指向一维数组的指针变量
   for(int i=0; i<3; i++)
   {
   	for(int j=0; j<4; j++)
   		cout<

7.const指针

  • 指向常量的指针变量(量不能变)
const int *p = &a;  // 不允许通过p修改a的值
*p = 10;  //非法
p = &b;   //合法
  • 常指针(指针不能变)
int * const p = &a; //不允许修改p的指向,但允许修改a的值;定义时要初始化
*p = 10;    //合法
p = &b; //非法
  • 指向常量的常指针(量不能变,指针也不能变)

1.常变量只能由 [指向常变量的指针变量] 指向它,不能用 [一般指针]

2.[指向常变量的指针变量] 也可以指向[一般变量],那么用该指针变量访问时有常变量的特征

3.[一般指针]只能指向[一般变量]

const int * const p = &a;
*p = 10;    //非法
p = &b; //非法

* void指针

  • 不指向任何类型 = 指向空类型 = 指向不确定的类型

可以把非void型的指针赋给void型指针变量,但不能把void型指针直接赋给非void型,必须先进行强制转换

int a = 3;
int *p1 = &a;
char *p2 = "new";
void *p3;
p3 = (void *)p1;    //将p1的值转换为void *类型,赋给p3
cout<<*p3<
int main()
{
	int a = 3;
	int *p1 = &a;
	void *p2 = p1;
	cout<

8.指针小结

定义 类型 含义
int *p; int * p为指针变量,指向int型数据
int *p[4]; int *[4] p为指针数组,指向4个元素都是int型指针的数组
int (*p)[4]; int (*)[4] p为指针变量,指向4个int数据的一维数组
int *p (); int * () p为正常函数,只是函数返回值为int型指针
int (*p)(); int (*)() p为指针变量,指向函数
int **p; int ** p为指针变量,指向一个指针(该指针指向int型数据)
int * const p int * const p是指针变量,常指针,指向不能变
const int *p
int const * p
const int * p是指针变量,指向常量,不能通过p修改值
const int * const p const int * const p是指针变量,指向不能变且也不能通过p修改值
void *p void * p是指针变量,基类型为空(void),不指向具体对象

9.引用(本质是指针常量)

变量的“别名”

int a;
int &b = a;     /* 引用只有声明,没有定义 */

int &b; /* 非法!引用声明的同时必须初始化 */

int a1, a2;
int &b = a1;    
int &b = a2;    /* 非法!引用声明后不能变更 */

int a[5];
int &b[5] = a;      //非法!不能建立引用数组
int &c[5] = a[0];   //非法!不能作为数组元素的别名

int a;
int &b = a;
int *p = b;     //非法!不能建立指向引用的指针

int a;
int &b = a;
int *p = &b;    //合法,&b是a的地址

10.参数传递

  • 变量名作形参(值传递)
void swap(int a, int b) {
    ...
}

void main(){
    int i=1, j=2;
    swap(i, j);    
}
  • 指针作形参(值传递)
void swap(int *p1, int *p2){
    ...
}

void main(){
    int i=1, j=2;
    swap(&i, &j);
}
  • 引用作形参(地址传递)
void swap(int &a, int &b){
    ...
}

void main(){
    int i=1, j=2;
    swap(i, j);
}

第7章 用户自定义数据类型

1.结构体类型(struct

struct Student
{
    int num;
    char name[20];
    char sex;
} student1 = {1001, "Zhangsan", 'M'};  //最后一个分号不能少!!!

Student student2 = {1002, "Wangwu", 'F'};

student1 = student2;    //相同结构体类型变量可相互赋值

student.num = 10010;    //"."是成员运算符

Student stu[3] = {1001, "Jack", 'M', 1002, "Peter", 'F', 1003, "mali", 'M'};   //结构体数组

Student *p = &student1; //结构体指针

等价
student1.num;
(*p).num;   // "." > "*"
p->num; 

// "->" > "++"
p->num++;   //得到num后,使用完加1
++p->num;   //等到num,加1后使用

2.链表

见考试大纲

3.newdelete

int *p1 = new int(100); //整型数  
int *p2 = new int[10];  //整型数组

delete p1;  //释放变量
delete [] p2;   //释放数组

4.枚举类型(枚举常量)

enum weekday{sun=7, mon=1, tue, wed, thu, fri, sat};    //tue为2,wed为3...往后加1

Weekday workday;

workday = tue;  //正确
workday = 2;    //错误
workday = (Weekday)2;   //C语言风格强制类型转化
workday = Weekday(2);   //C++风格强制类型转化

5.typedef声明新类型名

typedef int INTEGER;    
INTERGER I;

typedef int NUM[100];   //声明NUM为整型数组类型,包含100元素
NUM n;

typedef char *STRING;   //声明STRING为char *类型,即字符指针类型
STRING p;

typedef int (*POINTER)();   //声明POINTER为指向函数的指针,函数返回整数值
POINTER P;

typedef struct
{
    int month;
    int day;
    int year;
} DATE;     //声明DATE为结构体类型
DATE birthday;  

第9章 类和对象

1.构造函数

格式:

class Student
{
    private:
        int num;
        int score;
        char name[20];
    public:
        Student(){}     //无参构造函数
        Student(int n)  //一个参数的构造函数
        {
            num = n;
            score = 0;
            strcpy(name, "123");
        }
        Student(char nam[], int n=0, int score=0)   //默认参数的构造函数
        {
            num = n;
            score = n;
            strcpy(name, nam);
        }
        Student(int n, int s, char nam[]):num(n), score(s){strcpy(name, nam);}    //参数初始化列表
        
}

注意项:

  • 构造函数名必须与类名同名
  • 无返回值,无类型
  • 自动调用,用户不能显式调用
Student s;  //正确
Student s();    //错误!不能显式调用
  • 系统可自动生成无参构造函数
  • 数据成员是数组时,不能用参数初始化列表,要用正常函数体初始化
  • 带默认参数的构造函数,默认值在声明时给出
Student(int = 1001, int = 20);   //省略形参名声明合法
  • 一个类只能有一个默认构造函数
//下面两同时存在是错误的!二义性
Stduent();  
Student(int num=10, int score=0);

Student s;  //产生二义性
  • [构造函数重载] 和 [带默认参数的构造函数] 不能同时存在
//产生二义性
Box();
Box(int, int);
Box(int =10, int =10, int =10);

2.析构函数

4种情况下执行析构函数:

  • 函数内定义的局部对象:函数调用结束时释放前执行
  • 静态局部对象:main函数结束或exit函数结束时调用
  • 全局对象:main函数结束或exit函数结束时调用
  • new运算符动态建立的对象:在delete运算符释放该对象之前

析构函数不是删除对象,只是在撤销对象占用的内存之前完成一些清理工作

  • 析构函数没有返回值;没有函数类型;没有函数参数;不能被重载。
  • 一个类只能由一个析构函数
  • 编译系统会自动生成一个析构函数(do nothing)

3.构造函数、析构函数执行顺序

先构造的后析构,后构造的先析构

4.对象指针

Time *pt;
Time t1;
pt = &ti;

//等价于
(*pt).hour;
pt->hour;   // "->"是指针专用的

成员函数指针变量:

void (Time::*p2)();
p2=&Time::get_time;

要求三个匹配

  • 函数参数的类型和参数个数
  • 函数返回值的类型
  • 所属的类

5.this指针

每个成员函数都包含一个特殊的指针,指向本类对象的指针,他的值是当前被调用的成员函数所在的对象的起始地址。

//等价于
(*this).height
this->height
height

优先级:"." > “*”

6.共享数据的保护

常对象

//等价
Time const t1(12, 34, 46);
const Time t1(12, 34, 46);
  • 常对象必须初始化,整个对象生命周期中,数据成员的值不能改变
  • 常对象只能调用常成员函数
  • 常成员函数可以访问常对象中的数据成员,但不许改变他的值
  • 一定要改常对象中的某个数据成员值
mutable int count;  //mutable 可变数据成员

常数据成员(访问不限,不可改值)

class Time
{
    const int hour;    
}
  • 只能通过构造函数的参数初始化表初始化
  • 任何其他函数都不能改变它的值

常成员函数(访问不限,不可改值)

class Time
{
    void get_time() const;
}
  • 只能引用本类的数据成员,但不能改变值
  • 不能调用另一个非const的成员函数

7.指向对象的常指针

Time t1(19 ,12, 15);
Time * const p1;
p1 = &t1;
  • 指向对象的常指针,指向后,不允许改变指向
  • 但可以改变对象的数据成员
  • 做形参使用:保护指向在函数运行期不允许改变指向
void fun(Time * const t);

8.指向常对象的指针变量

同 指向常变量的指针变量

  • 常对象只能由[指向常对象的指针变量]指向,不能由[一般指针]指向
  • 使用[指向常对象的指针变量]时,有常量特征,不能改变值
  • 做形参使用:保护指向的对象在执行中不改变值
void fun(const Time *t);

9.对象的常引用

void fun(const Time &t);    //t所指向的变量的值不能变

10.对象的动态建立和释放

Time *t = new Time;
delete t;   //在释放内存空间前,自动调用析构函数

11.1 对象赋值

Time t1, t2;
...
t1 = t2;    //同属一类的两个对象可相互赋值
  • 类的数据成员中不能包括动态分配的数据,否则赋值有问题

11.2 对象复制

Box::Box(const Box&b)   //复制构造函数  注意参数是引用!!
{
    height = b.height;
    width =b.width;
}

...

Box box2(box1); //对象复制
  • 复制构造函数:只有1个参数,一般是const对象引用
  • 编译系统自动提供一个复制构造函数
对象复制 对象赋值
从无到有建立新对象 对已存在的对象进行赋值

对象复制的使用场景:

  • 1.建立新对象
Box box1(12, 15, 16);
Box box2(box1); //建立新对象box2
  • 2.函数参数为类对象
void fun(Box b) //函数调用时的虚实结合 
{
    ...
}
  • 3.函数的返回值是类的对象
Box f()
{
    Box box(12, 2, 4);
    return box; //赋值给一个“临时对象”返回给函数调用处
}

int main()
{
    Box b = f();
    return 0;
}

13.静态成员(static)

  • 归属于类,所有该类的对象所共有
class Box
{
    public:
        static int height;  //静态数据成员
        staitc int volume();    //静态成员函数
}

int Box::height = 10;   //静态数据成员只能在类体外进行初始化

int Box::volume()
{
    return height;
}

int main()
{
    Box a;
    
    //静态成员
    a.height;   //可以,通过对象引用
    Box::height;    //也可以,通过类名引用
    
    //静态成员函数
    a.volume(); 
    Box::volume();
    return 0;
}

静态成员

  • 内存中只占一份空间,程序运行开始时分配空间
  • 静态数据成员只能在类体外进行初始化
  • 静态数据成员未赋初值,默认赋0
  • 静态数据成员可被对象名引用,也可通过类名引用
  • 静态数据成员同样受访问权限控制

静态成员函数

  • 因:静态成员函数没有this指针
  • 果:静态成员函数不能直接访问本类非静态成员
  • 一定要访问非静态成员,通过对象名.非静态成员函数

14.友元

友元函数

  • 友元函数可以访问该类的私有成员

1.普通函数作为友元函数

class Time
{
    private:
        int hour; 
    public:
        friend void display(Time &);    //友元函数(不属于该类)
}

void display(Time &t)  //普通函数
{
    t.hour; //可以通过对象名访问私有成员
}
  • 引用私有成员要用对象名.数据成员
  • 没有this指针

2.一个类的成员函数,作为另一个类的友元函数

class Date; //提前引用声明

/* 客人 */
class Time  
{
    private:
        int hour;
    public:
        void display(Date &);   //正常成员函数
}

/* 主人 */
class Date  
{
    private:
            int hour;
    public:
        friend void Time::display(Date &);  //Time类中的display函数作为本类的友元函数
}

void Time::display(Date &d)
{
    d.hour;     //  别人家的私有数据成员
    hour;   //自己家的私有数据成员
}

注意项:

  • 声明友元函数时,加上该函数所属的类
  • 引用别人地私有成员要通过对象名.数据成员

友元类

//B是A的友元类,B中的所有成员函数都是A的友元函数
class B;
class A
{   
    public:
        friend class B; //B是A的友元类
}

class B
{
    ...    
}
  • 友元关系不能被继承
  • 友元关系是单向的,且不具有传递性

15.类模板

功能相同,只是数据类型不同

template 
class Compare
{
    public:
        Compare(T a, T b)   //构造函数
        {
            x=a;
            y=b;
        }
        
        T max() //成员函数类内定义 
        { return (x>y)?x:y; }
        
        T min();
        
    private:
        T x, y;
}

template
T Compare::min()     //类外定义
{ return (x>y)?x:y;}

int main()
{
    Compare f1(3, 7);  
    Compare f2(45.2, 30.1);
    return 0;
}

三部曲:

  • 1.写个实际的类
  • 2.虚拟类名替换具体类名
  • 3.第一行加入template

多个类型参数的情况:

template 
class Box
{
    ...
}
类模板 模板类
重点是模板,产生类的模子 重点是类,由模板产生的类

第10章 运算符重载(不在考纲内)

1.运算符重载规则

  • 运算符重载实质是函数重载
int operator+ (int a, int b)
{
    return (a+b);
}
  • 不能重载的5个运算符

. * :: sizeof ?:

  • 重载不能改变运算符运算对象(即操作数)个数
  • 重载不能改变运算符的优先级别
  • 重载不能改变运算符的结合性
  • 重载运算符的函数不能有默认的参数
  • 重载运算符必须至少一个参数是类的对象(或是类对象的引用),不能都是C++标准类型,以防止用户修改用于标准类型数据的运算符的性质
  • 用于类对象的运算符一般必须重载,除了“=” “&”

2.运算符重载函数作为类成员函数

第一个参数是this指针隐式调用的访问

class Complex
{
    private:
        double real;
        double imag;
    public:
        Complex operator+(Complex &c2); //成员函数运算符重载函数
};

Complex Complex::operator+(Complex &c2)
{
    Complex c;
    c.real = real + c2.real;
    c.imag = imag + c2.imag;
    return c;
}
  • “=” “[]” “()” "->"必须作为成员函数重载

3.运算符重载函数作为友元函数

没有this指针

class Complex
{
    private:
        double real;
        double imag;
    public:
        friend Complex operator+(Complex &c1, Complex &c2); //友元函数运算符重载函数
};

Complex operator+(Complex &c1, Complex &c2)
{
    Complex c;
    c.real = c1.real + c2.real;
    c.imag = c1.imag + c2.imag;
    return c;
}
  • “<<” “>>” 只能用友元函数重载

4.单目运算符重载

C++规定 有一个int型形参的重载函数,是后置自增自减运算函数

class Time
{
    private:
        int minute;
        int sec;
    public:
        Time operator++();  //++i
        Time operator++(int);   //i++
};

Time Time::operator++() //++i
{
    if(++sec >= 60)
    {
        sec-=60;
        ++minute;
    }
    return *this;
}

Time Time::operator++(int)  //i++
{
    Time temp(*this);   //临时对象 保存自加前的状态
    sec++;
    if(sec >= 60)
    {
        sec-=60;
        ++minute;
    }
    return temp;    //返回的是自加前的对象
}

5.重载流运算符(必须是友元函数)

class Complex
{
    private:
        double real;
        double imag;
    public:
        friend ostream& operator<< (ostream & , Complex & );
        friend istream& operator>> (istream & , Complex & );
};

ostream& operator<<(ostream &output, Complex& c)
{
    output<< "(" << c.real << "+" <>(istream& input, Complex& c)
{
    cout<<"please input real part and imaginary part of complex number:";
    input >> c.real >> c.imag;  
    return input;
}

6.转换构造函数

只有一个参数,如果有多个参数,他就不是转换构造函数

class Complex
{
    private:
        double real;
        double imag;
    public:
        Complex(double r)
        {
            real = r;   //将double型r转换为Complex类的对象
            imag = 0;
        }
}

7.类型转换函数

class Complex
{
    private:
        double real;
        double imag;
    public:
        operator double()
        {
            return real;
        }
}
  • 类型转换函数函数名为operator double
  • 返回值类型由函数名中指定类型名确定
  • 只能作为成员函数

第11章 继承与派生

1.派生类构成

  • 派生类把基类的全部成员接受过来(不包括构造函数析构函数
  • 调整从基类接收的成员

若派生类中声明一个与基类成员同名成员,则派生类中的新成员会覆盖基类的同名成员;对于成员函数要考虑函数重载的情况。

  • 派生类新增加的成员

2.权限

基类成员在基类的访问属性 继承方式 基类成员在派生类的访问属性
public public public
protected public protected
private public 不可访问
public protected protected
protected protected protected
private protected 不可访问
public private private
protected private private
private private 不可访问
派生类中访问属性 在派生类中 在派生类外部 在下一层公用派生类
public 可以 可以 可以
protected 可以 不可以 可以
private 可以 不可以 不可以
不可访问 不可以 不可以 不可以

3. 派生类的构造函数

派生类的构造函数

  • 继承过来的基类成员初始化工作由派生类构造函数负责
Class A     //基类
{
    private:
        int x, y;
    public:
        A(int a, int b)     //基类构造函数
        {
            x=a;
            y=b;
        }
};

/* 形式1: */
class B:public A    //派生类
{
    private:
        int z;
    public:
        B(int a, int b, int c):A(a, b)  //派生类构造函数
        {
            z=c;
        }
};

/* 形式2 */
class B:public A    //派生类
{
    private:
        int z;
    public:
        B(int a, int b, int c); //派生类构造函数声明
};

//类外定义
B::B(int a, int b, int c):A(a, b)
{
    z=c;
}

构造函数执行顺序:

  • 1.基类构造函数(基类数据成员初始化)
  • 2.子对象构造函数(子对象数据成员初始化)
  • 3.派生类构造函数(派生类数据成员初始化)

派生类只需写其上一层派生类的构造函数,不必每一层都写出来

派生类构造函数特殊形式

  • 当不需要对派生类新增成员操作时,派生类构造函数的函数体可为空
  • 基类中没有定义构造函数或定义了没有参数的构造函数,派生类构造函数可不写基类构造函数(系统默认调用基类默认构造函数)
  • 当基类、子对象、派生类都不需要参数,派生类可以省略显式构造函数,系统调用默认的构造函数
  • 当基类或子对象中定义了带参数的构造函数,则派生类必须显式的定义构造函数

4. 派生类的析构函数

析构函数执行顺序:

  • 1.派生类自己的析构函数(处理新增成员)
  • 2.子对象的析构函数(处理子对象)
  • 3.基类的析构函数(处理继承的成员)

5.多重继承

class D: public A, public B, protected C
{
    ...
};

多重继承构造函数调用顺序:

  • 1.调用基类的构造函数,调用次序按照他们继承时说明的次序(从左往右)
  • 2.调用子对象的构造函数,调用次序按照它们在类中说明的次序(从上到下)
  • 3.调用派生类的构造函数

多重继承析构函数调用顺序:

与多继承构造函数调用顺序相反

6.虚基类

在继承间接共同基类时,只保留一份基类成员

class A     //基类
{};
class B : virtual public A  //B是A的公有派生类,A是B的虚基类
{};
class C : virtual public A  //C是A的公有派生类,A是C的虚基类
{};
  • 虚基类实在声明派生类时,指定继承方式时声明的
  • 为保证虚拟类在派生类只继承一次,该虚基类的所有直接派生类都要声明为虚基类

虚基类的初始化

class A     //基类A
{
    A(int i){};
};

class B : virtual public A  //A是B的虚基类
{
    B(int n): A(n){}  
};

class C : virtual public A  //A是C的虚基类
{
    C(int n):A(n){}
};

class D : public B, public C    //正常继承
{
    D(int n): A(n), B(n), C(n){}    //D中要对所有基类初始化;重要!也就是还要对虚基类A初始化
};
  • 在最后的派生类中不仅要负责对直接基类初始化,还要负责虚基类的初始化!!!
  • 编译系统只执行最后派生类的构造函数调用,忽略虚基类的其他派生类(B和C)

7.基类和派生类的转换

只有公有派生类才是基类的真正子类型,它完整的继承了基类的功能

  • 1.派生类对象可以向基类对象赋值

儿子可以给爸爸赋值,反之不对

A a1;   //基类A
B b1;   //A的派生类B
a1 = b1;        //舍弃派生类自己独有的成员,“大材小用”
  • 2.派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化
A a1;
B b1;
A &r = b1;  //指的是b1中基类的那一部分的别名
  • 3.函数参数是基类对象或基类对象引用,实参可以用子类对象
void fun(A &r){}
fun(b1);    //正确 只输出派生类中的基类成员
  • 4.指向基类对象的指针变量也可以指向派生类对象(重要)

指向儿子中继承的那一部分成员

A *p1;
B b;
p1 = &b;    //指向B中继承A的那部分数据

8.小结

类的继承派生体现的是 “是”的关系

类的组合(子对象)体现的是“有”的关系

继承时纵向的,组合是横向的

第12章 多态性和虚函数

1.静态多态和动态多态

静态多态性 动态多态性
通过函数重载实现,编译时多态 通过虚函数实现,运行时多态
虚函数 函数重载
函数首部是相同的 函数首部是不同的(参数个数或类型)

2.虚函数(=基类声明虚函数+基类对象指针调用派生类方法)

同一类族中不同类的对象,对同一函数调用作出不同的响应

#include 
using namespace std;

class Student
{
	protected:
		int num;
		
	public:
		Student(int n):num(n){};
		virtual void display()  //虚函数
		{
			cout<display();
	p=&g;   //父类指针指向子类
	
	/* 
	    若display不是虚函数,那么只能调到父类的display方法;
	    若display在基类中被声明为虚函数,那么可以通过父类指针调到子类的display函数
	*/
	p->display();   
	return 0;
}

1001

1001 4500.5

使用虚函数

  • 类外定义虚函数不必要加上virtual
  • 基类成员函数被声明为虚函数,其派生类的同名函数自动成为虚函数,vitual可加可不加
  • 定义基类对象指针,指向同一类族;指谁调谁(调用的是指向对象的同名函数)
  • 虚函数+指向基类对象指针 = 动态多态性

3.虚析构函数

未声明为虚析构函数时,new 出来的对象,delete时只会调用基类的析构函数

#include 
using namespace std;

class Point
{
	public:
		Point(){}
		virtual ~Point()    //虚析构函数
		{
			cout<<"executing Point destructor"<

executing Circle destructor

executing Point destructor

  • 先调用派生类析构函数,再调用基类析构函数
  • 基类析构函数声明为虚函数后,该基类的所有派生类的析构函数都声明为虚函数
  • 构造函数不能被声明为虚函数!!!

4.纯虚函数

virtual float area() const = 0;
  • 纯虚函数没有函数体
  • 最后的"=0"不表示返回值为0
  • 这是个声明语句最后要加";"
  • 基类声明了纯虚函数,派生类没有对该函数定义,那么该虚函数在派生类中仍然是纯虚函数

5.抽象类

定义抽象类的目的:用它作为基类去建立派生类

  • 凡是包含纯虚函数的类都是抽象类
  • 包含纯虚函数的类是无法建立对象的

6.静态关联和动态关联

动态关联

  • 通过基类指针调用虚函数(pt->shapeName()),在运行阶段才能确定是哪一类的虚函数。

静态关联

  • 通过对象名调用虚函数(point.shapeName()),在编译阶段就能确定是哪一个类的虚函数

第13章 输入输出流

  • 标准输入输出(标准I/O)
  • 文件输入输出(文件I/O)
  • 串I/O

1.C++流库

两个基类

  • ios类(负责高层操作,面向用户)
  • streambuf类(负责低层操作,面向底层)

有关头文件

  • iostream:输入输出流
  • fstream:文件I/O
  • strstream:字符串I/O
  • stdiostream:混合C和C++
  • iomanip:格式化I/O用

2.标准输出流(ostream类的3个对象)

  • cout
  • cerr(只能输出显示器,不经过缓冲区)
  • clog(存放在缓冲区,当缓冲区满了或遇到endl,向显示器输出)

3.格式控制

  • 头文件#include

用流对象的成员函数控制

precision(n);   //实数精度为n为
width(n);   //字段宽度n位
fill(c);    //设置填充字符c

设置格式

setf(); //设置格式
unsetf();   //取消设置格式
参数:
iso::left
iso::dec    //整数基数是10
iso::oct    //整数基数是8
iso::hex    //整数基数是16
  • 成员函数width(n)setw(n)只对其后面第1个输出项有效

  • 要重新设置格式setf(),先取消usetf()

  • setf()可以设置多个 setf(ios::internal | ios::showpos)

成员函数put()

专门用于输出单个字符的成员函数put

cout.put('A');

4.标准输入流(cin)

键盘输入完按Enter后输入,跳过空格、Tab键、换行符等空白字符

if(!cin)
    cout<<"error";

5.字符输入的成员函数

  • get()读入一个字符

当遇到输入流中的文件结束符,函数返回EOF,即**-1**

#include 
using namespace std;

int main
{
    int c;
    while( ( c = cin.get() )!=EOF )
        cout.put(c);
    return 0; 
}

cin.get(ch);    //从输入流中读取一个字符,赋给ch

/* 等价 */
cin.get(字符数组, 字符个数n, 终止字符); //从输入流中读取n-1字符,赋给字符数组  n-1个有效字符和一个'\0'
cin.getline(字符数组,  字符个数n, 终止字符);

6.istream类的其他成员函数

  • eof()

文件结束返回 非零
文件没结束返回 0

#include 
using namespace std;

int main()
{
    char c;
    while( !cin.eof() ) //eof()为假表示未遇到文件结束符
        if( (c=cin.get())!=' ' )    //检查读入的字符是否为空格字符
            cout.put(c);
    return 0;
}
  • peek() 观察
c = cin.peek(); //返回指针当前指向的字符
  • putback()
cin.putback(ch);    //将ch插在当前指针之后的位置(注意是插入,不是覆盖)
  • ignore()
cin.ignore(n, 终止字符) ;   //跳过n个字符或者 终止字符以前的都被忽略
#include 
using namespace std;

int main()
{
	char ch[20];
	cin.ignore(18, 'A');
	cin.get(ch, 20, '/');
	cout<

abcA123/

123

7.文件

文件两大类:

  • 程序文件
  • 数据文件

根据文件中数据的组织形式分:

  • ASCII文件 1个字节(8位)存放1个字符(文本文件、字符文件)
  • 二进制文件(内部格式文件、字节文件)

I/O功能

  • 低级I/O:以字节为单位输入输出(二进制形式)
  • 高级I/O:有意义单位输出(ASCII字符)

8.文件流类和文件流对象

  • ifstream:从istream派生来。支持从磁盘文件输入
  • ofstream:从ostream派生来。支持向磁盘文件输出
  • fstream: 从iostream派生来。支持对磁盘文件的输入输入

1.打开文件

ofstream outfile;
outfile.open("f1.dat", ios::out); //以输出方式打开一个文件
方式 作用
ios::in 以输入方式打开文件
ios::out 以输出方式打开文件(默认方式),如果已有此名字的文件,则将其原有的内容全部清除
ios::app 以输出方式打开文件,追加写
ios::ate 打开已有文件,文件指针指向文件末尾
ios::trunc 打开文件,若如果文件存在,则删除其中的全部数据;如果不存在,则建立新文件。ios::out方式的默认格式
ios::binary 二进制格式打开文件,若没指定则默认ASCII方式打开
ios::nocreate 打开一个已有文件,如文件不存在,则打开失败
ios::noreplace 如果文件不存在则建立新文件,如果文件存在则操作失败
ios::in | ios::out 以输入输出方式打开文件,可读可写
ios::out | ios::binary 以二进制输出方式打开文件
ios::in | ios::binary 以二进制输入方式打开文件
  • 不能组合互斥的方式,如:ios::nocreate | ios::noreplace
//检测文件打开是否失败
if(outfile.open("f1.dat", ios::app) ==0)
    cout<<"open error";
    
if( !outfile.open("f1.dat", ios::app) )
    cout<<"open error";
    
outfile.close();    //关闭文件

9.对ASCII码文件的操作

例. 键盘输入10个整数送到数组,在将数组存到磁盘文件

#include 
#include 
using namespace std;

int main()
{
	int a[10];
	ofstream outfile("d:\\f1.dat", ios::out);	//输出格式打开文件 
	if( !outfile )	//判断是否打开成功,打开失败时返回0 
	{
		cerr<<"open error!"<>a[i];
		outfile<

10.对二进制文件的操作

与ASCII码文件不同

既能做输入又能输出的文件

istream &read(char *buffer, int len);   //读
ostream &write(const char *buffer, int len);    //写
#include 
#include 
using namespace std;

struct Student
{
	char name[20];
	int num;
	int age;
	char sex;
};

int main()
{
	Student stu[2] = {{"Li", 1001, 18, 'f'}, {"Wang", 1002, 17, 'f'}};
	
	//输出到磁盘文件 
	ofstream outfile("d:\\stu.dat", ios::binary);
	if(!outfile)
	{
		cerr<<"open error"<

1001 Li 18 f

1002 Wang 17 f

11.文件指针有关函数

infile.seekg(100);  //输入文件位置标记向前移动100字节位置
infile.seekg(-50, ios::cur);    //输入文件中位置标记从当前位置后移50字节
outfile.seekp(-75, ios::end);   //输出文件中位置标记从文件尾后移50字节

你可能感兴趣的:(考研,C++)