Skip to content

基类指针 ​

基类指针是指向基类类型的指针,可以用于操作派生类对象,是实现运行时多态的关键机制。

cpp
class Animal {
public:
    int a;
	virtual void speak() const;
	virtual ~Animal() = default;  // 虚析构函数,避免内存泄漏
};

class Dog : public Animal {
public:
	void speak() const override;
};

class Cat : public Animal {
public:
    int b;
	void speak() const override;
};

Animal* ptr;                // 基类指针 ptr 是一个指向基类类型的指针
Animal* ptr = new Dog(); 	// ptr 是一个指向 Dog 类对象的指针
ptr->speak(); 				// 调用 Dog 对象的 speak 方法
Animal* ptr = new Cat();	// ptr 是一个指向 Cat 类对象的指针
ptr->speak();				// 调用 Cat 对象的 speak 方法
  • Animal*:静态类型
  • ptr 具有动态类型,可以根据指向的对象类型而变化
  • 编译器可以根据 ptr 的动态类型,在运行期选择调用哪个函数

当 ptr 指向 Dog 类对象,其动态类型是 指向 Dog 对象的指针;当 ptr 指向 Cat 类对象时,其动态类型是 指向 Cat 对象的指针;当 ptr 指向 Animal 类对象时,其动态类型就是静态类型;这就是 多态性。

注意事项 ​

1. 基类析构函数必须为 virtual ​

基类指针不负责派生类对象的析构,当通过基类指针删除派生类对象时只调用基类析构函数,将导致资源泄漏:

cpp
Animal* p = new Dog();
delete p;	// 若无虚析构,只会调用 Animal 的析构函数

// 正确做法:基类析构函数设为 virtual
class Animal {
public:
	virtual void speak() const;
	virtual ~Animal() = default;  // 虚析构函数
};

2. 对象切片问题 ​

直接通过对象赋值会丢失派生类信息,叫做对象切片:

cpp
Dog d;
Animal a = d;	// 对象切片:只保留 Dog 中的 Animal 部分
a.speak();		// 输出的是 Animal 类的 speak(),多态失效

3. 空指针检查 ​

使用前应检查指针是否为空:

cpp
Animal* ptr = getPointer();
if (ptr) {
    ptr->speak();
}

4. 访问受限 ​

基类指针只能访问基类中定义的成员,无法直接访问派生类新增的成员:

cpp
Animal* ptr = new Cat();
ptr->b = 10; // 错误:Animal 没有成员 b