一、C++三大特性

记忆口诀

三大特性记分明,封装继承多态行;
封装数据和操作,权限控制安全定;
继承复用加扩展,子类父类心相印;
多态接口行为异,编译运行两类型。

核心概念

  • 封装:数据与操作打包,通过访问权限控制暴露(public/private/protected),提高安全性,隐藏实现细节
  • 继承:子类拥有父类属性和行为,可在原有基础上扩展,实现代码复用
  • 多态:同一种接口不同行为,分编译时多态(函数重载、运算符重载)和运行时多态(虚函数+继承)

代码示例

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 Animal {
protected:
string name;
public:
Animal(string n) : name(n) {}
virtual void speak() { // 虚函数实现多态
cout << name << " is making a sound." << endl;
}
};

// 继承
class Dog : public Animal {
public:
Dog(string n) : Animal(n) {}
void speak() override { // 重写父类方法
cout << name << " says: Woof!" << endl;
}
};

// 多态体现
void animalSpeak(Animal* a) {
a->speak(); // 根据对象真实类型调用方法
}

二、重载与重写

记忆口诀

重载重写要分清,作用时机不相同;
重载同类名相同,参数不同编译定;
重写继承父虚函,函数签名要一致;
运行绑定用多态,override关键字记。

重载(Overload)

  • 定义:同一作用域内,函数名相同但参数列表不同
  • 特点:编译期绑定(静态多态),与返回值无关

重写(Override)

  • 定义:派生类重写基类虚函数,函数签名完全相同
  • 特点:运行期绑定(动态多态),需用virtual和override关键字

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 重载示例
class Printer {
public:
void show(int x) { cout << "打印 int: " << x << endl; }
void show(double x) { cout << "打印 double: " << x << endl; }
};

// 重写示例
class Animal {
public:
virtual void speak() { cout << "动物在叫" << endl; }
};

class Dog : public Animal {
public:
void speak() override { cout << "狗在叫:汪汪汪" << endl; }
};

三、访问修饰符

记忆口诀

访问权限有三种,public、private和protected;
公有成员随便用,私有只能内部通;
保护成员加一层,子类也能来访问。

核心概念

  • public:类内外都可访问
  • private:仅类内部可访问
  • protected:类内部和派生类可访问

四、多重继承

记忆口诀

多重继承多基类,功能强大风险藏;
菱形继承问题现,二义性来把人伤;
虚继承来解难题,共享基类实例创。

核心概念

  • 定义:一个类从多个基类继承属性和行为
  • 问题:菱形继承会导致二义性(同一基类在派生类中出现多次)
  • 解决:虚继承(virtual)确保基类在最终派生类中只存在一个实例

代码示例

1
2
3
4
5
// 虚继承解决菱形问题
class Animal { /* ... */ };
class Mammal : virtual public Animal { /* ... */ };
class Bird : virtual public Animal { /* ... */ };
class Bat : public Mammal, public Bird { /* ... */ };

五、多态的实现机制

记忆口诀

多态实现靠虚函,四个步骤记心间;
基类声明加virtual,派生重写override添;
基类指针指派生,调用方法运行辨;
底层依赖vtable,虚指针来把表联。

实现步骤

  1. 基类声明虚函数(virtual关键字)
  2. 派生类重写虚函数(override关键字)
  3. 使用基类指针/引用指向派生类对象
  4. 通过基类指针/引用调用虚函数

底层机制

  • 虚函数表(vtable):每个含虚函数的类都有一个vtable,存储虚函数地址
  • 虚指针(vptr):每个对象内含一个vptr,指向所属类的vtable
  • 动态绑定:运行时通过vptr找到vtable,调用对应函数

六、成员与静态成员

记忆口诀

成员静态要区分,所属对象与类分;
成员函数带this,访问变量实例跟;
静态函数无this,只能访问静态存;
静态变量类共享,类外定义初始化。

成员函数与成员变量

  • 成员函数:属于对象,可访问成员变量,通过对象调用
  • 成员变量:每个对象一份,随对象创建和销毁

静态成员函数与静态成员变量

  • 静态成员函数:属于类,无this指针,不能直接访问非静态成员
  • 静态成员变量:所有对象共享,需在类外定义和初始化

代码示例

1
2
3
4
5
6
7
8
9
10
class MyClass {
public:
int memberVar; // 成员变量
static int staticVar; // 静态成员变量声明

void memberFunc() { /* 成员函数 */ }
static void staticFunc() { /* 静态成员函数 */ }
};

int MyClass::staticVar = 0; // 静态成员变量定义

七、构造函数与析构函数

记忆口诀

构造析构特殊函,生命周期来掌管;
构造函数无返回,与类同名可重载;
默认带参拷贝委,各司其职来创建;
析构函数波浪号,资源释放要做好。

构造函数

  • 定义:创建对象时自动调用,用于初始化对象
  • 特点:函数名与类名相同,无返回类型,可以重载
  • 种类:默认构造、带参构造、拷贝构造、委托构造

析构函数

  • 定义:对象销毁时自动调用,用于释放资源
  • 特点:函数名前加~,无参数,不能重载

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyClass {
public:
// 默认构造函数
MyClass() { /* 初始化 */ }

// 带参数构造函数
MyClass(int val) { /* 参数初始化 */ }

// 拷贝构造函数
MyClass(const MyClass &other) { /* 深拷贝或浅拷贝 */ }

// 委托构造函数
MyClass() : MyClass(42) { /* 委托给带参构造 */ }

// 析构函数
~MyClass() { /* 释放资源 */ }
};

八、虚函数与纯虚函数

记忆口诀

虚函数有实现,派生可选重写;
纯虚函数等号零,强制派生实现它;
抽象类含纯虚函,不能实例只能继承;
接口规范靠它们,多态机制顶呱呱。

虚函数

  • 定义:基类中用virtual声明的函数,可在派生类中重写
  • 特点:有默认实现,派生类可选重写,包含虚函数的类可实例化

纯虚函数

  • 定义:没有实现的虚函数(virtual func() = 0)
  • 特点:派生类必须实现,包含纯虚函数的类为抽象类,不能实例化

代码示例

1
2
3
4
5
6
7
8
9
10
11
// 虚函数
class Base {
public:
virtual void virtualFunc() { /* 有实现 */ }
};

// 纯虚函数与抽象类
class AbstractBase {
public:
virtual void pureVirtualFunc() = 0; // 无实现
};

九、虚析构函数

记忆口诀

虚析构函数很重要,多态删除离不了;
基类指针指派生,delete时析构全调用;
若无虚析构来帮忙,资源泄漏把祸闯。

核心概念

  • 定义:带virtual关键字的析构函数
  • 作用:确保通过基类指针删除派生类对象时,正确调用派生类析构函数,避免资源泄漏

代码示例

1
2
3
4
5
6
7
8
9
class Base {
public:
virtual ~Base() { /* 基类析构 */ }
};

class Derived : public Base {
public:
~Derived() override { /* 派生类析构 */ }
};

十、不能声明为虚函数的函数

记忆口诀

虚函数有例外,以下函数不能改;
构造函数最明显,对象未建vptr未;
静态友元普通函,无继承无this在;
内联函数编译展,动态绑定冲突来。

不可声明为虚函数的函数

  1. 构造函数:对象未完全创建,vptr未初始化
  2. 普通函数(非成员函数):无继承特性,无意义
  3. 静态成员函数:属于类而非对象,无this指针
  4. 友元函数:C++不支持友元函数继承
  5. 内联成员函数:内联编译与虚函数动态绑定矛盾

十一、深拷贝与浅拷贝

记忆口诀

深拷贝,资源复制彻底分;
每个对象有自己的内存根;
浅拷贝,资源共享问题生;
重复释放要小心。

深拷贝

  • 定义:完全复制对象及其内部动态分配的资源
  • 特点:新对象与原对象完全独立,需手动管理内存

浅拷贝

  • 定义:仅复制对象值,不复制内部动态分配资源
  • 特点:新对象与原对象共享资源,可能导致重复释放

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 深拷贝示例
class DeepCopyExample {
public:
int *data;

DeepCopyExample(const DeepCopyExample &other) {
data = new int(*(other.data)); // 复制资源
}

~DeepCopyExample() { delete data; }
};

// 浅拷贝示例(默认拷贝行为)
class ShallowCopyExample {
public:
int *data; // 仅复制指针值,不复制指向的内容
};

十二、运算符重载

记忆口诀

运算符重载函数藏,本质还是函数样;
算术关系非成员,赋值下标必须藏;
递增递减建议藏,箭头必须成员当;
特殊运算符别乱抢,原有语义要保障。

核心概念

  • 本质:函数重载,函数名为"operator运算符"
  • 调用方式:可直接使用运算符(a+b)或函数调用形式(operator+(a,b))

规则与建议

  • 算术/关系运算符:建议非成员函数,保持对称性
  • 赋值运算符:必须是成员函数
  • 下标运算符:必须是成员函数,建议提供const和非const版本
  • 递增/递减运算符:建议成员函数,区分前置/后置版本
  • 箭头运算符(->):必须是成员函数

特殊注意

  • 不建议重载:逗号、取地址、逻辑与、逻辑或(破坏原有语义或求值顺序)
  • 输入/输出运算符:建议非成员函数,需定义为友元以访问私有成员
  • Lambda表达式:被编译器翻译为未命名类的未命名对象,捕获列表对应类的数据成员