在 C++ 中,基类指针可以指向派生类对象的核心原因是 **“is-a” 关系 **(派生类是基类的一种特殊形式)。这种机制是实现多态的基础,下面从前提、语法、特性、应用及注意事项展开说明。
一、前提:public 继承
基类指针能指向派生类对象的前提是子类必须以public方式继承父类(这一点非常重要)。
- 若为
private或protected继承,派生类与基类的 “is-a” 关系被隐藏,编译器不允许基类指针指向派生类对象(隐式转换被禁止)。
二、语法:直接赋值
当满足public继承时,基类指针指向派生类对象的语法非常直接:用派生类对象的地址为基类指针赋值。
示例代码:
#include <iostream>
using namespace std;
// 基类
class Base {
public:
int baseNum;
void baseFunc() {
cout << "Base::baseFunc()" << endl;
}
};
// 派生类(public继承基类)
class Derived : public Base {
public:
int derivedNum; // 派生类新增成员
void derivedFunc() { // 派生类新增函数
cout << "Derived::derivedFunc()" << endl;
}
};
int main() {
Derived d; // 定义派生类对象
Base* ptr = &d; // 基类指针指向派生类对象(合法,因public继承)
return 0;
}
从主函数中,可以看出,先声明一个子类Derived的实例对象,然后再声明一个父类的指针,然后直接将子类的值,赋值给基类的指针。(这里需要注意:实例对象的变量名!=首个成员变量的地址,结构体的实例对象也是如此,变量名实例对象的变量名!=首个成员变量的地址)
三、核心特性:访问范围受限
基类指针指向派生类对象后,只能访问派生类中 “从基类继承的成员”(包括成员变量和成员函数),无法直接访问派生类新增的成员(除非强制类型转换,但不推荐)。
原因:编译器仅知道指针的类型是 “基类”,编译时会根据指针类型(而非指向对象的实际类型)检查访问权限。
示例说明:
int main() {
Derived d;
Base* ptr = &d;
// 合法:访问从基类继承的成员
ptr->baseNum = 10;
ptr->baseFunc();
// 错误:无法访问派生类新增成员(编译器不知道这些成员的存在)
// ptr->derivedNum = 20; // 编译报错
// ptr->derivedFunc(); // 编译报错
return 0;
}
四、关键应用:结合虚函数实现多态
当基类中存在虚函数时,基类指针指向派生类对象后,调用虚函数会触发动态绑定(运行时根据对象实际类型调用对应的函数版本),这是多态的核心。
示例代码:
class Base {
public:
// 基类声明虚函数
virtual void vfunc() {
cout << "Base::vfunc()" << endl;
}
};
class Derived : public Base {
public:
// 派生类重写(override)虚函数
void vfunc() override {
cout << "Derived::vfunc()" << endl;
}
};
int main() {
Derived d;
Base* ptr = &d; // 基类指针指向派生类对象
ptr->vfunc(); // 输出:Derived::vfunc()(动态绑定,调用派生类版本)
return 0;
}
原理:虚函数通过 “虚函数表” 实现,基类指针指向派生类对象时,会访问派生类的虚函数表,从而调用派生类的重写版本。
五、注意事项
-
析构函数需设为虚函数若基类指针指向派生类对象,且通过该指针
delete对象时,若基类析构函数不是虚函数,会导致派生类部分未被析构(内存泄漏)。因此需将基类析构函数声明为虚函数:cpp
运行
class Base { public: virtual ~Base() { cout << "Base::~Base()" << endl; } // 虚析构 }; class Derived : public Base { public: ~Derived() override { cout << "Derived::~Derived()" << endl; } }; int main() { Base* ptr = new Derived(); delete ptr; // 正确:先调用Derived::~Derived(),再调用Base::~Base() return 0; } -
避免强制转换的风险若通过
static_cast<Derived*>将基类指针强制转换为派生类指针,虽然能访问派生类新增成员,但如果指针实际指向的是基类对象,会导致未定义行为(如访问非法内存)。
总结
基类指针指向派生类对象是 C++ 多态的基础,其核心是public继承下的 “is-a” 关系。通过该机制,可统一用基类指针管理不同派生类对象,并结合虚函数实现 “同一接口,不同实现”。使用时需注意访问范围、虚析构函数等细节,避免内存问题。
2524

被折叠的 条评论
为什么被折叠?



