期末面向对象复习大纲

C++ 面向对象程序设计 知识点总大纲

I. 基础语法与控制流 (C++ Basics, Ch 2)

A. C++ 概述与词法结构

  1. C++ 产生和发展历史(C++11/14/17/20 等标准)。
  2. C++ 语言的最小词法单元:关键字、标识符、文字(字面量)、操作符、分隔符、空白符 。
  3. 标识符命名规则(起始字符、组成、大小写敏感性)。

B. 数据类型、常量与变量

  1. C++ 基本数据类型:整数类型(int, short, long, 符号/无符号)、浮点数类型(float, double)、字符类型(char, ASCII 编码)、布尔类型(bool), 。
  2. 常量分类:整数常量(十进制、八进制、十六进制、后缀)、字符常量、字符串常量(以 \0 结尾), 。
  3. 变量定义与初始化(包括列表初始化)。
  4. 符号常量(const)。
  5. 常量表达式(const,值在编译阶段确定)。
  6. 类型别名(typedefusing)。
  7. 枚举类型(enum:不限定作用域枚举(默认值 0, 可关系运算, 整数不能直接赋值给枚举变量),以及 enum class(枚举类) 的强作用域和类型转换限制 , , , 。
  8. 类型推断(auto)和类型获取(decltype), 。

C. 运算符与表达式

  1. 算术运算、自增自减(++, --)。
  2. 赋值运算(=)和复合赋值运算符(+= 等)。
  3. 逗号运算和逗号表达式(求解顺序及结果)。
  4. 关系运算 ==, !=, <, > 等)。
  5. 逻辑运算(!, &&, ||)及其 短路特性
  6. 条件运算符(?:
  7. sizeof 运算和位运算(&, |, ^, ~, <<, >>
  8. 运算符优先级与结合性

D. 流程控制与 I/O

  1. if 语句和嵌套 if 结构(if-else 配对原则).
  2. switch 语句(执行顺序、case 值类型限制、break 的作用), .
  3. while 语句和 do-while 语句(执行顺序的区别), .
  4. for 语句(包括基于范围的 for 循环).
  5. breakcontinue 语句的作用 .
  6. I/O 流概念(cin, cout),插入符 << 和提取符 >> , .
  7. I/O 流操纵符(如 endl, setw, setprecision).

II. 函数 (Functions, Ch 3)

  1. 函数的定义、调用、函数原型(声明)。
  2. 函数的嵌套调用 。
  3. 函数参数传递:值传递(单向)与引用传递(双向), 。
  4. 常引用作参数:保障实参数据的安全,防止被调函数意外修改实参 。
  5. 递归调用:定义、递推过程、回归过程,递归出口(终止条件)。
  6. 含有可变参数的函数(initializer_list 类型), 。
  7. 内联函数(inline):作用(编译时替换)、限制(不能有循环/switch)、定义位置 。
  8. 带默认参数的函数:设置默认值、形参位置限制(必须在最右侧)、默认值设置位置(原型声明或函数定义), 。
  9. 函数重载:概念、重载规则(形参类型或个数必须不同)、不以返回值区分 。
  10. C++ 系统函数(如 cmath 中的 sqrt, sin, pow 等).

III. 类与对象核心 (OOP Core, Ch 4)

A. 类和对象的定义

  1. 类是对象的抽象,对象是类的实例 , 。
  2. 类的语法形式、成员访问权限(public, private, protected), 。
  3. 类内初始值(用于数据成员初始化)。
  4. 成员函数的定义:类中声明原型,类外定义实现(使用类名限定),内联成员函数 。

B. 构造函数与析构函数

  1. 构造函数:作用(初始化对象)、形式(与类名同名,无返回值),可重载、可带默认参数 。
  2. 默认构造函数:参数表为空或全部参数有默认值的构造函数;编译器隐含生成(如果未定义其他构造函数)或显式指示(=default), 。
  3. 委托构造函数(避免重复代码)。
  4. 复制构造函数:形参为本类的常引用(const 类名 &对象名)。
  5. 复制构造函数被调用的三种情况:
    • 定义对象时以本类另一个对象作为初始值 。
    • 函数的形参是类的对象(值传递)。
    • 函数的返回值是类的对象 。
  6. 隐含的复制构造函数(成员依次复制)。
  7. 禁用复制构造函数(=deleteprivate)。
  8. 析构函数:作用(清理工作)、形式(函数名加 ~),在对象生存期结束时自动调用 。

C. 类的组合与结构体

  1. 类组合:类成员是另一个类的对象 。
  2. 组合类的构造函数设计:必须在初始化列表中对成员对象进行初始化 。
  3. 组合类成员的构造顺序:按照成员在类体中定义的顺序进行初始化(与初始化列表顺序无关), 。
  4. 结构体(struct):特殊形式的类,默认访问权限为 public,常用于只有数据且无复杂操作的类型 。
  5. 联合体(union):成员共用同一内存单元,任何两个成员不会同时有效 。

IV. 数据的共享与保护 (Data Sharing, Ch 5)

  1. 作用域:函数原型作用域、局部(块)作用域、类作用域、文件作用域 , 。

  2. 可见性:内层作用域中声明的同名标识符会隐藏外层作用域的标识符 , 。

  3. 对象的生存期

    • 静态生存期:与程序运行期相同(全局变量、static 变量)。
    • 动态生存期(局部生存期):限于块作用域中,离开作用域结束 。
  4. 类的静态数据成员static):为该类的所有对象共享,具有静态生存期

  5. 静态数据成员的声明与初始化(一般在类外使用 :: 初始化), 。

    1. 类的静态变量是否全局可见?
      关于类的静态数据成员(例如 int Cat::numOfCats)的可见性,需要区分其生存期和作用域:
      • 生存期(Life): 它们具有静态生存期,从程序启动时存在到程序结束,因此它们在“时间”上是全局的。
      • 可见性(Visibility): 它们不是像传统的全局变量那样“全局可见”。静态数据成员的声明是在类作用域内。
      ◦ 在类外部访问它们时,即使它们是 public 的,也必须通过类名和作用域操作符 :: 来访问(例如 CLIENT::GetServerName()),。
      ◦ 如果静态成员被声明为 private(为了封装),则它们只能通过该类的静态成员函数(例如 Cat::getNumOfCats())来间接访问,。
      因此,更准确的说法是:类的静态变量具有全局寿命,但其可见性受到类的作用域限制,必须通过特定的方式(类名::)才能访问。
  6. 类的静态函数成员static):主要用于处理静态数据成员,可以直接通过类名 :: 调用;不能直接访问非静态成员 , , 。

  7. 友元机制friend 关键字,破坏数据封装;友元函数(可访问私有/保护成员)、友元类(关系是单向的), , 。

  8. 共享数据的保护(const:常对象(必须初始化,不能更新),常成员函数(不更新对象数据成员),常引用(防止意外更改实参), 。

  9. 常成员函数与非常成员函数可以重载

    1. 常数据成员举例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #include <iostream>
    using namespace std;
    class A {
    public:
    A(int i);
    void print();
    private:
    const int a;
    static const int b; //静态常数据成员
    };
    const int A::b=10;
    A::A(int i) : a(i) { }
    void A::print() {
    cout << a << ":" << b <<endl;
    }
    int main() {
    //建立对象a和b,并以100和0作为初值,分别调用构造函数,
    //通过构造函数的初始化列表给对象的常数据成员赋初值
    A a1(100), a2(0);
    a1.print();
    a2.print();
    return 0;
    }
    //运行结果:
    //100:10
    //0:10

  10. 多文件结构:类声明(.h)、类实现(.cpp)、主函数(main().cpp)。

  11. 编译预处理命令#include#define(宏定义)、条件编译(#if, #else, #endif, #ifdef, #ifndef)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 变量的生存期与可见性
#include<iostream>
using namespace std;
int i = 1; // i 为全局变量,具有静态生存期。
void other() {
static int a = 2;
static int b;
// a,b为静态局部变量,具有全局寿命,局部可见。
//只第一次进入函数时被初始化。
int c = 10; // C为局部变量,具有动态生存期,
//每次进入函数时都初始化。
a += 2; i += 32; c += 5;
cout<<"---OTHER---\n";
cout<<" i: "<<i<<" a: "<<a<<" b: "<<b<<" c: "<<c<<endl;
b = a;
}
int main() {
static int a; //静态局部变量,有全局寿命,局部可见。
int b = -10; // b, c为局部变量,具有动态生存期。
int c = 0;
cout << "---MAIN---\n";
cout<<" i: "<<i<<" a: "<<a<<" b: "<<b<<" c: "<<c<<endl;
c += 8; other();
cout<<"---MAIN---\n";
cout<<" i: "<<i<<" a: "<<a<<" b: "<<b<<" c: "<<c<<endl;
i += 10; other();
return 0;
}
/*
---MAIN---
i: 1 a: 0 b: -10 c: 0
---OTHER---
i: 33 a: 4 b: 0 c: 15
---MAIN---
i: 33 a: 0 b: -10 c: 8
---OTHER---
i: 75 a: 6 b: 4 c: 15
*/

V. 数组、指针与动态内存 (Arrays & Pointers, Ch 6)

A. 数组

  1. 数组的定义(一维/多维)和元素访问 。
  2. 数组的存储:元素在内存中顺次连续存放 , 。
  3. 数组的初始化:部分初始化时未初始化的元素自动赋 0;局部非静态数组存在垃圾数据 , 。
  4. 数组名是常量,不可以被赋值。
  5. 数组作为函数参数:传递的是数组首地址(退化为指针),形参对实参数组的改变会直接影响实参数组 。
    1. 注意数组退化问题:对于形参来说,其实形参 int a[]与 int *p是等价的,数组在传入函数会被退化为首元素的地址/首行的地址;但是注意二维数组退化只会退化第一维,比如int\只能退化为int[]\,因为第二维也是编译器索引的依据!
  6. 对象数组:定义、初始化(调用构造函数)、访问 ,
    1. 定义:给出全部数据这时候可以不写出方括号里的具体数字,二维数组可以不写第一维具体数字,但二维数组的第二维一定要写,这是查找的步长依据
    2. 可以不给出全部所需元素的,但一定要写出所有具体数字
    3. 用[]访问,数组名本身就是首元素的地址,因此 int a[]; int *p=a;是对的;

B. 指针

  1. 指针概念:内存地址,用于间接访问内存单元;指针变量用于存放地址 。

  2. 数组名是数组首元素的内存地址,是一个常量,不能被赋值 , 。

  3. 指针的初始化与赋值:必须赋地址,不能是普通整数(除了 0 或 nullptr), 。

  4. **nullptr**:C++11 引入的类型安全的空指针 。

  5. void* 指针:可被赋予任何类型对象的地址,但解引用前需强制转换 。

  6. 指向常量的指针const T *p):不能通过指针修改数据,但指针可指向其他对象

    1
    2
    3
    4
    5
    6
    7
    8
    const int a=10;
    int b=2;
    const int *p1;
    int *p2;
    p1=a;//√
    p2=a //X 常量变量只能被指向常量的指针指向
    *p1=10; //X 错,指向常量的指针保证只对指向的变量可读,但是不能通过该指针修改指向的地址存储的值
    p1=b; //√,但是指向常量的指针可以重新指向别的地址,而且不一定是const声明的变量
  7. 指针常量T *const p):指针的值(地址)不能改变,但可以通过它修改所指对象(除非对象是 const

    1
    2
    3
    4
    5
    const int a =10;
    int b=2;
    int const *p=b; //无所谓指向的地址存储的变量能不能改变,重点在于不能改变指向的地址
    p=a;//×,不能改变指向的地址
    *p=90; //√,改变地址存储的变量值,但是本身指向的地址没有变,合法
  8. 指向包含 static关键字的变量,有static int * p

  9. 指针的算术运算:指针与整数的加减(指向第 n 个数据的起始位置)、++ / -- 运算(指向下一个/前一个完整数据的起始)。

    1. 注意,如果a是数组名,a++/a–是不合法的,正如前面所说,数组名其实就可以算作首元素的地址的别名,地址怎么运算?
  10. 指针数组:数组元素是指针类型 。

  11. 函数指针:指向程序代码存储区,用于实现函数回调 。

    1. int compute(int a,int b,int (*func)(int,int))//注意要写上传入函数的形参和返回类型
             {
          return func(a,b);
      }
       int max(int a,int b)
       {
           return a>=b? a:b;
       }
       int main()
       {
           //调用方法1,取函数地址
           compute(1,2,&max);
           //调用方法2,声明一个指向函数的指针,传入指针
           int (*maxPtr)(int,int);//形参的写法
           maxPtr=max;
           compute(1,2,*maxPtr);
       }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      12. **对象指针**:通过 `类名 *对象指针名` 定义;通过 `->` 访问成员

      1. (\*p).func() <=>p->func();
      2. 利用对象指针可以弥补 `前向声明`的不足

      ```c++
      #include <iostream>
      using namespace std;
      class B; //前向声明,只能表示有这个类,具体怎么样不知道
      class A{
      public:
      void f(B,b);//√,这里只是声明函数没有让B的实例b进行具体的操作
      void fPtr();
      private:
      B b; //错误,不能定义b,我们现在还不知道B的构造函数是什么样的
      B* b;//可以,指向B类的地址,没有直接调用B的任何成员
      };
      class B{
      public:
      void g(A,a);
      };
      void A::f(B b)
      {
      cout<<"A::f called"<<endl;
      }
      void A::f()
      {
      b->gPtr();//注意这里函数的具体定义已经写在B类定义后了
      }
      void B::g(A a)
      {
      cout<<"B::g called"<<endl;
      }
      void B::gPtr()
      {
      cout<<"B::gptr called"<<endl;
      }
      int main()
      {
      }


13.this 指针:==隐含于类的每一个非静态成员函数中==,指向当前对象(成员函数所操作的对象,毕竟直接通过类名调用成员函数嘛,是通过实例),用于区分形参和成员变量,以及实现链式调用
1. 例, Point类中getX()函数中调用 return x;相当于 return this->x
2. 我们可以用来区分形参与成员变量,this->age=age;
3. 实现链式调用

1
2
3
#include <iostream>
class Builder {
public:
        Builder& setX(int val) {
            x = val;
            return *this;  // Enable chaining
        }
        Builder& setY(int val) {
            y = val;
            return *this;
        }
    void print() const {
    Qstd::cout << "x: " << this->x << ", y: " << this->y << std::endl;
    }

    private:
        int x = 0, y = 0;
    };

    int main() {
        // Usage:
        Builder b;
        b.setX(10).setY(20);  // Fluent interface
        b.print();
        return 0;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
14. **指针函数**(返回指针的函数):==返回的地址必须是有效且合法的==(如==动态分配的地址或主函数中定义的数组元素地址==),不能返回非静态局部变量的地址(非静态局部变量再函数调用完后就会被销毁,内存被释放)

1. 在子函数中通过动态内存分配new操作取得的内存地址返回给主函数是合法有效的,但是内存分配和释放不在同一级别,要注意不能忘记释放,避免内存泄漏
```c++
//错误例子
int main(){


    int* function();
    int* ptr= function();
    *ptr=5; //危险的访问!
    cout << *ptr << endl; //未定义行为    
    return 0;
}
int* function(){
    int local=0; //非静态局部变量作用域和寿命都仅限于本函数体内
    return &local;
} //函数运行结束时,变量local被释放
//运行结果: 36551 segmentation fault(core dumped) ./6out
` `c++
//正确例子
//省略前文…………
int main(){
    int array; //主函数中定义的数组
    int* search(int* a, int num);
    for(int i=0; i<3; i++){
      cin>>array[i];
}
  //将主函数中数组的首地址传给子函数
    int* ptr= search(array, 3);
//输出数组中第一个0元素
//省略后文…………
}
//指针a指向主函数中定义的数组
int* search(int* a, int num){
    for(int i=0; i<num; i++){
      if(a[i]==1){
//返回的地址指向的元素是在主函数中定义的
        return &a[i];
      }
    }
    return 0;
} //函数运行结束时,a[i]的地址仍有效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//用new 关键字动态分配地址
#include<iostream>
using namespace std;
int main(){
    int* newIntVar(); //函数声明
    int* intptr = newIntVar();
    cout << *intptr << endl; //输出动态分配的地址
    *intptr = 5; //访问的是合法有效的地址
    cout << *intptr << endl;
    delete intptr; //如果忘记在这里释放,会造成内存泄漏
    return 0;
}
int* newIntVar (){
    // int* p = new int();
    int* p = new int;
    return p; //返回的地址指向的是动态分配的空间
}//函数运行结束时,p中的地址仍有效

C. 动态内存分配与字符串

  1. 动态申请内存new Tnew T[n](返回 T 类型指针), 。

  2. 释放内存delete pdelete[] p(必须是 new 返回的指针), 。

  3. 应用:动态创建对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    	#include <iostream>
    using namespace std;
    class Point{
    public:
    Point() : x(0), y(0) {
            cout<<"Default Constructor called."<<endl;
        }
        Point(int x, int y) : x(x), y(y) {
            cout<< "Constructor called."<<endl;
        }
        ~Point() { cout<<"Destructor called."<<endl; }
        void move(int newX, int newY) {
            x = newX;
            y = newY;
            }
       private:
       int x,y;
    };
    int main() {
        cout << "Step one: " << endl;
        Point *ptr1 = new Point;   //调用默认构造函数
        delete ptr1;   //删除对象,自动调用析构函数
        cout << "Step two: " << endl;
        ptr1 = new Point(1,2);  //调用传参构造函数
        delete ptr1;    
       
        return 0;
    }
    /*
    运行结果
    Step One:
    Default Constructor called.
    Destructor called.
    Step Two:
    Constructor called.
    Destructor called.
    //如果是普通的实例对象,要再作用域语句执行完之后才会销毁对象
    //使用new delete可以自己控制对象的生存期
    */
  4. 应用:动态创建多维数组
    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    char (*fp);//创建一个指向有三个char元素的指针fp
    fp=new char;//创建一个2d数组,两个元素,返回第一个元素地址的指针;

    //辨析
    char (*fp);
    //创建一个指向有三个char元素的指针fp,那么fp是一个指针名
    char *fp;
    //创建一个拥有三个char类型指针的元素的数组,fp代表第一个元素(char类型指针)的地址
    char **fp /*生成指向char类型指针的指针(交错数组),
    更加自由,用它构建的二维数组,可以不是一整块内存分配的,
    而且数组的每一行的元素个数可以不尽相同*/

    • 可以在访问数组元素前检查下标是否越界
      • 用assert来检查,assert只在调试时生效
  5. 智能指针(C++11):unique_ptr(不共享资源)、shared_ptr(共享)、weak_ptr(不影响资源释放)。

  6. 深层复制与浅层复制:浅层复制只复制指针本身,深层复制复制指针所指向的内容,避免二次释放和数据共享问题。

    1. 复制指针本身让两个指针指向同一个地址,如果通过其中一个指针修改变量,释放内存,另一个指针指向的值也会发生相应变化,
    2. 深层复制,是回到复制的精髓,我们真正要复制的是指针指向的对象的值,然后用一个新的指针指向一个有相同值的新的地址。就像我们拷贝变量也是在新的地址存储相同的值,不是复制该指针成员本身,而是将指针所指对象进行复制。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    	//例如6-18
    ……
    ~ArrayOfPoints() {
            cout << "Deleting..." << endl;
            delete[] points;    
        }
        ……

    /*
    运行结果如下Please enter the number of points:2
    Default Constructor called.
    Default Constructor called.Copy of pointsArray1:
    Point_0 of array2:5,10
    Point_1 of array2:15,20After the moving of pointsArray1
    Point_0 of array2:25,30
    Point_1 of array2:35,40
    Deleting….Destructor called.
    Destructor called.Deleting
    …接下来程序出现运行错误。
    先删掉了Array1,Array1的析构函数调用了Point类的析构函数,把array1和Array2的共同指向的Point 变量给删除了,后面Array2再删除自己的Point类成员,删除失败了
    //只靠系统生成的复制构造函数不够,我们要显式构建一个,复制所指的对象
    */

    //修改:新的复制构造函数
    ....
    ArrayOfPoints(const ArrayOfPoints& v); //复制构造函数
    .....
    };
    ArrayOfPoints::ArrayOfPoints(const ArrayOfPoints& v){
        size = v.size;
        points = new Point[size];
        for (int i = 0; i < size; ++i) {
            points[i] = v.points[i];
        }
    }


  7. 案例:函数返回含有指针成员的对象,使用深层复制构造函数,将要返回的局部对象转移到主调函数,省去了构造和删除临时对象的过程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    	#include <iostream>
    using namespace std;
    class IntNum {
    public:
        IntNum(int x = 0) : xptr(new int(x)) { // 构造函数
            cout << "Calling constructor..." << endl;
        }
        IntNum(const IntNum &n) : xptr(new int(*n.xptr)) { // 复制构造函数
            cout << "Calling copy constructor..." << endl;
        }
        IntNum(IntNum &&n) : xptr(n.xptr) { // 移动构造函数
            n.xptr = nullptr;
            cout << "Calling move constructor..." << endl;
        }
        ~IntNum() { // 析构函数
            delete xptr;
            cout << "Destructing..." << endl;
        }
    int getInt() { return *xptr; }
    private:
      int *xptr;
    };
    // 返回值为 IntNum 类对象
    IntNum getNum() {
        IntNum a;
        return a;
    }

    int main() {
        cout << getNum().getInt() << endl;
        return 0;
    }
    //运行结果
    /*
    Calling constructor...
    0
    Destructing...
    */


  8. 移动构造(C++11):通过移动语义将临时对象的资源控制权转移给目标对象,减少不必要的复制,提高性能。

    1. 移动构造函数class_name ( class_name && )
  9. string:对字符数组操作的封装,重载了运算符(如 + 和比较运算符),解决了 C 风格字符串的缺点。

    1. C 风格字符串:以字符数组存储,以 \0 结尾,操作繁琐,易越界 。例如char str="program; char str = { 'p', 'r', 'o', 'g', 'r', 'a', 'm', '\0' } char str[] = "program";
    2. string 类常用构造函数 string(); string(const char* s); string(const string& rhs)
    3. s[i] 访问串中下标为i的字符
  10. string 类的输入:cin >> s 以空格为分隔符;getline(cin, s) 输入整行字符串 。

    1. getline可以输入整行字符串(要包括string头文件),输入字符串时,可以使用其它分隔符作为字符串结束的标志(例如逗号、分号),将分隔符作为getline的第3个参数即可,例如:getline(cin, s2, ',');
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      #include <iostream>
      #include <string>
      using namespace std;
      int main() {
          for (int i = 0; i < 2; i++){
              string city, state;
              getline(cin, city, ','); //<string>库里面
              getline(cin, state);//输入的Beijing/San Francisco已经被前面的city获取了
              cout << "City:" << city << “  State:" << state << endl;
          }
          return 0;
      }
      /*
      运行结果:
      Beijing,China
      City: Beijing State: China
      San Francisco,the United States
      City: San Francisco State: the United States
      */

  11. vector 对象:封装动态数组,元素个数可在运行时改变,支持下标越界检查。

VI. 继承与派生 (Inheritance, Ch 7)

  1. 继承的基本概念:特殊类继承一般类,实现代码复用 , , 。
  2. 继承访问控制(公有、私有、保护), 。
  3. 派生类的构造和析构函数(构造顺序、析构逆序)。

VII. 多态性 (Polymorphism, Ch 8)

A. 运算符重载

  1. 运算符重载的意义:对已有运算符赋予多重含义,作用于自定义类型 。
  2. 不能重载的运算符:., .*, ::, ? :
  3. 重载为成员函数:参数个数 = 原操作数个数 - 1(左操作数隐式为 this)。
  4. 重载为非成员函数:参数个数 = 原操作数个数;至少有一个自定义类型参数;可声明为友元访问私有成员 。
  5. 前置单目运算符(如 ++p):重载为成员函数时无形参
  6. 后置单目运算符(如 p++):重载为成员函数时,必须有一个 int 形参用于区分(该形参无实际意义)。
  7. 流插入 << 和流提取 >> 运算符:必须重载为非成员函数(通常为友元)以使左操作数为流对象 , 。
    1. 双目运算符 B重载后,
    1. 表达式operand1 B operand2 等同于operator B(operand1, operand2)
      2. 前置单目运算符 B重载后, 表达式 B operand 等同于operator B(operand)
      3. 后置单目运算符 ++和–重载后, 表达式 operand B 等同于operator B(operand, 0)

B. 虚函数与抽象类

  1. 虚函数virtual):实现运行时多态(动态绑定)的基础。
  2. 虚函数必须是非静态的成员函数;构造函数不能是虚函数,析构函数可以是虚函数
  3. 虚表(vtable)与虚指针(vptr):多态类有虚表,对象有虚指针指向虚表,实现动态绑定 。
  4. 虚函数覆盖规则:派生类函数若与基类虚函数满足特定条件(名称、参数、const 限定符等相同),则自动确定为虚函数并覆盖基类函数 。
  5. 虚析构函数必须掌握,确保通过基类指针删除派生类对象时,派生类和基类的析构函数都能被调用,防止内存泄漏 , , 。
  6. 纯虚函数:基类中声明的虚函数,没有具体操作内容,声明格式为 virtual T func() = 0; , 。
  7. 抽象类:带有==纯虚函数==的类,只能作为基类使用,不能定义抽象类的对象 , 。
  8. **override**:显式声明函数覆盖基类的虚函数,有助于编译器发现“未覆盖”错误(如忘记 const 限定符), , 。
  9. **final**:避免类被继承,或基类的虚函数被覆盖 , 。

VIII. 模板与群体数据 (Templates & Containers, Ch 9)

  1. 函数模板:解决重载函数逻辑一致但类型不同导致的冗余问题,创建通用功能的函数 。
  2. 函数模板的定义形式:template <typename T>
  3. 模板实例化限制:只有能进行模板中运算的类型,才能作为类型实参(自定义类需重载相应运算符), 。
  4. 类模板:为类声明一种模式,使数据成员、参数、返回值能取任意类型 , 。
  5. 类模板的默认模板实参 。
  6. 类模板的成员函数定义:在类模板以外定义时,需使用 template <模板参数表>类名<模板参数标识符列表>::函数名 的形式 , 。
  7. 群体概念:由多个数据元素组成的集合体,分为线性群体和非线性群体 。
  8. 线性群体:元素按位置排列有序,访问方法包括直接访问(数组)、顺序访问(链表)。
  9. 动态数组类模板:如 Array 类,实现运行时可变大小的数组,通常包含动态分配的指针成员 T *list , 。
  10. vector 对象:用类模板实现的动态数组,提供自动管理和下标越界检查 , 。
  11. 链表:一种动态数据结构,由结点组成,结点在运行时动态生成 。
  12. 单链表结点:包含数据域和指向下一个结点的指针(next), 。
  13. LinkedList 类(链表):包含表头指针 front、表尾指针 rear 以及用于遍历的游标指针 prevPtrcurrPtr
  14. 链表的基本操作:生成、插入(前/后)、查找、删除(头/当前)、遍历、清空 , .
  15. 栈(Stack)和队列(Queue):可以通过链表类实现,队列遵循先入先出(FIFO)原则 , 。
  16. 常函数重载在链表结点类中的应用:提供普通版本和 const 版本(如 nextNode()data()),以保障 const 对象的数据安全。

期末面向对象复习大纲
https://username.github.io/2025/12/23/oop review/
作者
AKIRA
发布于
2025年12月23日
许可协议