C++中如何使用子类的指针指向父类的对象

在 C++ 中,基类指针可以指向派生类对象的核心原因是 **“is-a” 关系 **(派生类是基类的一种特殊形式)。这种机制是实现多态的基础,下面从前提、语法、特性、应用及注意事项展开说明。

一、前提:public 继承

基类指针能指向派生类对象的前提是子类必须以public方式继承父类(这一点非常重要)

  • 若为privateprotected继承,派生类与基类的 “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;
}

原理:虚函数通过 “虚函数表” 实现,基类指针指向派生类对象时,会访问派生类的虚函数表,从而调用派生类的重写版本。

五、注意事项

  1. 析构函数需设为虚函数若基类指针指向派生类对象,且通过该指针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;
    }
    
  2. 避免强制转换的风险若通过static_cast<Derived*>将基类指针强制转换为派生类指针,虽然能访问派生类新增成员,但如果指针实际指向的是基类对象,会导致未定义行为(如访问非法内存)。

总结

基类指针指向派生类对象是 C++ 多态的基础,其核心是public继承下的 “is-a” 关系。通过该机制,可统一用基类指针管理不同派生类对象,并结合虚函数实现 “同一接口,不同实现”。使用时需注意访问范围、虚析构函数等细节,避免内存问题。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值