33.总结
33.1 知识点
学习的主要内容
强调
33.2 核心要点速览
面向对象脚本概念
- 核心理念:万物皆对象,用类抽象特征和行为。
封装
类和对象
核心要点 | 说明 |
---|---|
类的定义 | 用class 声明,包含成员变量、构造/析构函数、成员方法,是对象的模板。 |
对象实例化 | - 栈上:ClassName obj; - 堆上: ClassName* ptr = new ClassName(); |
内存分布 | 类定义在代码段,对象实例存储在栈或堆,成员变量占用实际内存。 |
访问修饰符(3P)
修饰符 | 作用 | 访问范围 |
---|---|---|
public |
公共成员,类内外均可访问。 | 类内、类外、子类(继承时) |
private |
私有成员,仅类内部可访问(默认修饰符)。 | 仅限类内 |
protected |
保护成员,类内和子类可访问。 | 类内、子类(继承时) |
成员方法
核心要点 | 说明 |
---|---|
声明与定义 | - 声明在类内,定义可在类内(内联)或类外(.cpp )。 |
内联函数 | 用inline 修饰,编译时插入代码,减少函数调用开销,适合短函数。 |
this指针 | 指向当前对象,用于区分参数与成员变量(如this->age = age; )。 |
构造函数与析构函数
类型 | 构造函数 | 析构函数 |
---|---|---|
声明 | ClassName(); / ClassName(int a); |
~ClassName(); |
作用 | 初始化对象成员,可重载。 | 释放对象资源(如堆内存),不可重载。 |
调用时机 | 实例化对象时自动调用。 | 对象释放时自动调用(栈对象出作用域/堆对象delete )。 |
静态成员
类型 | 静态成员变量 | 静态成员函数 |
---|---|---|
声明 | static int count; |
static void func(); |
初始化 | 在.cpp 中定义:int ClassName::count = 0; |
直接定义在类内或.cpp ,无this 指针。 |
访问方式 | ClassName::count / obj.count |
ClassName::func() / obj.func() |
特性 | 所有对象共享,生命周期贯穿程序运行。 | 不能访问非静态成员,属于类而非对象。 |
下面是优化后的表格,展示了 C++ 中 const
成员变量、const
成员函数和 const
修饰函数参数的详细对比:
const 成员变量 vs const 成员函数
特性 | const 成员变量 | const 成员函数 |
---|---|---|
声明示例 | const int age = 18; const int id; (需初始化列表) |
void print() const; |
初始化规则 | - 声明时直接赋值 - 构造函数初始化列表赋值 |
- 在函数声明和定义处均需加 const 后缀 |
修改限制 | 初始化后不可修改(即使在构造函数体内也不可赋值) | - 不可修改非 mutable 成员变量- 可修改 mutable 成员变量 |
调用限制 | 无特殊限制 | - 只能被 const 对象调用- 非 const 对象可调用 |
典型用途 | - 固定常量值(如 PI) - 对象标识(如 ID) |
- 访问器(getter)方法 - 不修改状态的操作 |
const 修饰函数参数
参数类型 | 语法示例 | 限制 | 作用 |
---|---|---|---|
基本类型 | void func(const int i); |
函数内不可修改参数值(值传递) | 防止意外修改,无实际数据保护(值拷贝) |
指向常量的指针 | void func(const int* ptr); |
可改指针指向,不可改指向的值 | 保护数据不被修改,允许指针重定向 |
指针常量 | void func(int* const ptr); |
不可改指针指向,可改指向的值 | 固定指针指向,允许修改数据 |
指向常量的指针常量 | void func(const int* const ptr); |
不可改指针指向和指向的值 | 完全保护数据和指针指向 |
常量引用 | void func(const int& ref); |
不可修改引用对象,避免拷贝开销 | 高效传递对象,保护数据不被修改 |
友元
类型 | 友元函数 | 友元类 | 友元成员函数 |
---|---|---|---|
声明 | friend void func(Class& obj); |
friend class FriendClass; |
friend void FriendClass::func(Class& obj); |
本质 | 全局函数,需在类中声明为friend |
整个类被声明为友元,所有成员可访问 | 某类的成员函数被声明为友元,允许访问 |
作用 | 允许单个函数访问类的私有/保护成员 | 允许整个FriendClass类访问另一个类的私有/保护成员 | 允许某个类的特定成员函数访问私有/保护成员 |
访问范围 | 函数内可访问目标类的私有/保护成员 | 友元类的所有成员函数均可访问目标类成员 | 仅被声明的成员函数可访问目标类成员 |
调用方式 | 直接调用(全局函数) | 通过友元类对象调用成员函数 | 通过友元类对象调用被声明的成员函数 |
注意事项 | - 非类成员,需通过对象参数访问 - 无this指针 |
- 友元关系单向(A是B的友元≠B是A的友元) - 不继承 |
- 需明确作用域(FriendClass::func )- 属于友元类的成员 |
代码示例 | class A { friend void f(A& a); }; void f(A& a) { a.privateMember; } |
class A { friend class B; }; class B { void access(A& a) { a.privateMember; } }; |
class A { friend void B::access(A& a); }; class B { void access(A& a) { a.privateMember; } }; |
嵌套类
核心要点 | 说明 |
---|---|
定义 | 在类内声明的类,如class Outer { class Inner { ... }; }; |
访问限制 | 受外层类访问修饰符影响(public 可外部使用,private 仅限外层类内)。 |
独立性 | 与外层类相互独立,可定义自己的成员和方法。 |
命名空间
核心要点 | 说明 |
---|---|
作用 | 避免命名冲突,封装相关功能(如namespace Math { int add(); } )。 |
声明 | namespace Name { int val; void func(); } |
使用 | - 限定访问:Name::val - 导入: using namespace Name; |
嵌套 | 可多层嵌套,如namespace A::B::C { ... } ,用A::B::C::func(); 访问。 |
运算符重载
核心要点 | 说明 |
---|---|
语法 | 返回类型 operator符号(参数列表) ,如Point operator+(const Point& p); |
类型 | - 成员函数:左操作数为类对象 - 非成员函数:可定义其他类型左操作数 |
常见重载 | + - * / == != ++ -- [] () 等,不可重载. :: ?: 等符号。 |
注意 | 需保持运算符原始语义,避免滥用,建议成对实现关系运算符(如== 与!= )。 |
继承
继承的基本规则
特性 | 说明 | 注意 |
---|---|---|
“是一个”关系 | 子类获得父类字段、方法、属性;不含构造、析构、友元 | 支持多继承(C++)、传递性(前提是访问权限允许) |
语法 | class 子 : 访问修饰 父 { … }; |
访问修饰符决定基类成员在子类中的新可见性 |
同名隐藏 | 子类定义同名成员会隐藏基类所有同名重载 | 用 父类::member 或在子类写 using 父类::member; 恢复访问 |
禁止继承 | C++ 用 final ,C# 用 sealed |
只对类生效,不影响已有成员访问 |
继承方式与访问控制
父类原级别 | public 继承后 | protected 继承后 | private 继承后 |
---|---|---|---|
public | public | protected | 不可访问 |
protected | protected | protected | 不可访问 |
private | 不可访问 | 不可访问 | 不可访问 |
继承后的构造函数和析构函数
阶段 | 执行顺序 | 要点/注意 |
---|---|---|
构造 | ① 递归调用顶层基类 ② 构造子类成员(字段/对象,声明顺序) ③ 执行子类自身构造体 |
默认调用基类无参构造;若无,则初始化列表必须显式调用带参基构造;C++ 可 using 父::父; 引入基类构造 |
析构 | ① 执行子类自身析构 ② 析构子类成员 ③ 调用各级基类析构 |
析构不继承,顺序与构造相反 |
多重继承
特性 | 说明 | 关键点/注意 |
---|---|---|
语法 | class 子 : 父1, 父2 { … }; |
构造按声明顺序调用;析构逆序;若同名需 父::成员 指定 |
同名二义 | 多个父有同名成员 | 必须 父类::member |
菱形继承 & 虚继承
类型 | 问题或方案 | 关键点/注意 |
---|---|---|
普通菱形继承 | B、C 各继 A ⇒ D 得到两份 A | 成员访问二义、内存冗余 |
虚继承 | class B : virtual public A … |
D 只保留一份 A;虚基类由最底层派生类负责构造(中间 A(...) 不生效);访问时需额外偏移/指针跳转,开销略增 |
继承中的友元 & 运算符重载
特性 | 继承行为 | 关键点/注意 |
---|---|---|
友元 (friend ) |
不会被继承 | 父子友元关系独立;子类私有/保护成员仍不可由父友元访问 |
运算符重载 (operator ) |
会被继承 | 子类可直接使用父类重载运算符 |
里氏替换原则
场景 | 表现 | 关键点/注意 |
---|---|---|
栈上替换 | GameObject obj = Player(); |
对象切片(子类部分丢失)、不可再转回子类,不推荐 |
堆上替换 | GameObject* p = new Player(); |
不切片,可 dynamic_cast<子*>(父*) 安全下转;常用于多态场景 |
多态
虚函数与多态实现
特性 | 说明 | 关键点/注意事项 |
---|---|---|
多态本质 | 同一父类的不同子类对象,执行相同方法时表现不同行为(运行时多态) | 通过虚函数(virtual )和重写(override )实现 |
虚函数声明 | virtual 返回值 函数名(参数) {} (需在父类声明,子类用override 重写) |
- virtual 不可省略,override 建议保留(编译期检查重写正确性)- 重写时参数/返回值必须与父类一致 |
保留父类逻辑 | 子类重写函数中用父类名::函数名() 显式调用父类实现 |
Son::Test() { Father::Test(); cout << “子类逻辑”; } |
多态调用场景 | 父类指针/引用指向子类对象时,自动调用子类重写方法(堆上里氏替换) | 栈上对象切片会丢失多态性(仅调用父类方法) |
虚析构函数
特性 | 说明 | 核心作用 |
---|---|---|
必要性 | 父类析构函数设为虚函数,确保通过父类指针释放子类对象时调用子类析构。如果父类没有设置子类析构不会调用。只需保证最上层父类有虚析构即可。 | 避免子类资源未释放(如堆内存)导致内存泄漏 |
执行顺序 | 子类析构 → 父类析构(与构造顺序相反) | 多层继承时只需顶层基类析构为虚函数即可 |
栈上调用 | 栈上对象按正常析构顺序调用(先子后父),但切片时子类特有析构可能不执行 | Father* f = new Son(); delete f; // 正确调用Son析构 Father f2 = Son(); // 切片,仅调用Father析构 |
抽象类与纯虚函数
特性 | 说明 | 规则 |
---|---|---|
抽象类定义 | 包含至少一个纯虚函数的类(不能直接实例化,仅作基类) | class Shape { virtual void draw() = 0; // 纯虚函数 }; |
纯虚函数语法 | virtual 函数() = 0; (无函数体,子类必须重写) |
子类未实现纯虚函数则自身也为抽象类 |
应用场景 | 定义接口规范(如图形基类、游戏对象基类) | 抽象类可包含普通函数和成员变量,子类继承后需实现所有纯虚函数 |
禁止重写(final关键字)
特性 | 说明 | 语法示例 |
---|---|---|
禁止类继承 | class 类名 final {}; (防止类被继承) |
class FinalClass final {}; |
禁止函数重写 | 虚函数后加final (virtual void func() override final; ) |
class Father { virtual void func() final; // 子类不可重写 }; |
虚函数表(V-Table)
特性 | 说明 | 底层原理 |
---|---|---|
本质 | 存储虚函数指针的数组,每个含虚函数的类对应一张表 | 类的所有对象共享虚函数表,对象含虚表指针(vptr)指向该表 |
内存位置 | 存储在数据段/常量段(全局共享) | 对象sizeof 会增加一个指针大小(64位8字节) |
多继承实现 | 每个父类对应一张虚表,子类对象含多个vptr指向各父类虚表 | 子类虚函数放入第一个父类虚表中(主流编译器实现) |
面向对象关联知识点
接口(C++模拟实现)
核心要点 | 说明 | 实现方式 |
---|---|---|
本质 | 行为的抽象规范,C++中通过纯虚函数的抽象类模拟接口 | class IFly { virtual void Fly()=0; }; (仅含纯虚函数的抽象类) |
命名规范 | 接口类名前缀加I (如IFly ) |
class IWalk { virtual void Walk()=0; }; |
继承与实现 | 子类继承接口类后必须实现所有纯虚函数 | class Bird : public IFly { void Fly() override { ... } }; |
应用场景 | 统一管理不同类型但有相同行为的对象(如飞机、鸟、超人都实现IFly 接口) |
IFly* obj[2] = {new Plane(), new Bird()}; (里氏替换原则应用) |
多继承同名虚函数
核心要点 | 说明 | 实现规则 |
---|---|---|
同名处理 | 多继承时父类若有同名虚函数,子类只需重写一次即可覆盖所有父类版本 | class Father { virtual void Eat(); }; class Mother { virtual void Eat(); }; class Son : public Father, public Mother { void Eat() override { Father::Eat(); Mother::Eat(); } } |
底层原理 | 多个父类虚函数表中的该函数指针均指向子类重写的函数 | 子类虚函数表中该函数地址统一指向子类实现,调用时自动分发 |
空类型的内存占用
核心要点 | 说明 | 示例 |
---|---|---|
空类大小 | 空类/结构体占1字节(确保对象有唯一内存地址) | sizeof(EmptyClass) → 1(64位/32位平台均为1) |
继承优化 | 子类包含成员时,空基类的1字节会被优化(不额外占用空间) | class Base {}; // sizeof=1 class Derived : Base { int i; }; // sizeof=4(Base的1字节被优化) |
含虚函数空类 | 包含虚函数的空类占8字节(64位平台,虚表指针大小) | class VirtualEmpty { virtual void Fun(); }; → sizeof=8 (64位) |
结构体与类的区别
核心要点 | 结构体(struct) | 类(class) |
---|---|---|
默认访问权限 | public(成员和继承默认均为public) | private(成员和继承默认均为private) |
设计初衷 | 侧重数据集合(如坐标、配置数据) | 侧重面向对象封装(数据+行为) |
继承语法 | struct Son : Father {}; (默认public继承) |
class Son : Father {}; (默认private继承) |
应用场景 | 简单数据结构(如struct Point { int x,y; } ) |
复杂对象(如class Player { int hp; void Attack(); } ) |
字符数组与string对比
核心要点 | 字符数组(char[]) | string类 |
---|---|---|
内存管理 | 固定长度,需手动管理(易越界) | 动态扩容,自动管理内存 |
安全性 | 无边界检查,易导致缓冲区溢出 | 有边界检查,安全系数高 |
常用操作 | 需要调用C函数(如strcat_s 、strcpy_s ) |
支持运算符重载(+ 、+= )和成员函数(append 、substr ) |
空终止符 | 需手动保证\0 终止,否则打印乱码 |
自动管理终止符,size() 返回有效长度 |
推荐场景 | 底层开发、嵌入式(空间敏感) | 日常字符串处理、C++标准库交互 |
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com