C++简明教程(11)(类的继承,虚函数)

以下是一份关于类在继承派生中的使用教程:

一、继承和派生的概念与作用

概念
继承是面向对象编程中的一个重要特性,它允许一个类(子类或派生类)从另一个类(父类或基类)获取属性和方法。子类可以继承父类的成员变量和成员函数,并且可以根据自身的需求进行扩展或修改。

作用

  • 代码复用:避免重复编写相同的代码,提高开发效率。例如,如果有多个类都具有一些共同的属性和行为,将这些共同部分提取到父类中,子类只需继承父类并根据需要进行个性化定制,减少了代码冗余。
  • 层次结构清晰:通过继承建立起类的层次关系,使得程序的结构更加清晰、易于理解和维护。例如,在一个图形绘制系统中,可以有一个基类 Shape,然后派生出 CircleRectangle 等子类,这样的层次结构能很好地组织代码逻辑。

二、继承的类型

在 C++ 中,继承主要有以下三种类型:

1. 公共继承(public

  • 特点:父类的 publicprotected 成员在子类中保持相同的访问级别,父类的 private 成员在子类中不可直接访问。
  • 示例代码
class Parent {
   
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;
};

class Child : public Parent {
   
public:
    void accessMembers() {
   
        publicVar = 10;  // 可以直接访问父类的 public 成员
        protectedVar = 20;  // 可以直接访问父类的 protected 成员
        // privateVar = 30;  // 错误,不能访问父类的 private 成员
    }
};
  • 使用场景:当希望子类完全继承父类的接口,并对外公开父类的部分接口时,通常使用公共继承。这是最常用的继承方式,符合“is-a”的关系,即子类是一种特殊的父类。

2. 保护继承(protected

2.1 protected 关键字

protected 关键字用于修饰类的成员,被 protected 修饰的成员在类的内部可以被访问,在子类中也可以被访问,但在类的外部不能被直接访问。

示例代码

class Base {
   
protected:
    int protectedData;
public:
    void setProtectedData(int value) {
   
        protectedData = value;
    }
    int getProtectedData() {
   
        return protectedData;
    }
};

class Derived : public Base {
   
public:
    void modifyProtectedData() {
   
        protectedData += 10;  // 可以在子类中访问父类的 protected 成员
    }
};

int main() {
   
    Base b;
    // b.protectedData = 5;  // 错误,在类外部不能直接访问 protected 成员
    b.setProtectedData(5);
    std::cout << b.getProtectedData() << std::endl;

    Derived d;
    d.modifyProtectedData();
    std::cout << d.getProtectedData() << std::endl;
    return 0;
}

注意事项

  • 合理使用 protected 关键字可以在一定程度上控制类的成员的访问权限,既保证了子类能够继承和访问必要的实现细节,又防止了外部类的随意访问,有助于封装性的维护。但如果过度使用 protected,可能会导致类的层次结构过于紧密耦合,不利于代码的维护和扩展,因此需要谨慎权衡。
2.2 protected继承
  • 特点:父类的 publicprotected 成员在子类中都变为 protected 成员,父类的 private 成员在子类中仍然不可直接访问。
  • 示例代码
class Parent {
   
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;
};

class Child : protected Parent {
   
public:
    void accessMembers() {
   
        publicVar = 10;  // 此时父类的 public 成员在子类中变为 protected,可在子类成员函数中访问
        protectedVar = 20;  // 可以访问父类的 protected 成员
        // privateVar = 30;  // 错误,不能访问父类的 private 成员
    }
};

int main() {
   
    Child c;
    // c.publicVar = 5;  // 错误,在类外部不能访问 protected 成员
    return 0;
}
  • 使用场景:当希望子类继承父类的实现细节,但不希望外部类直接访问父类的某些接口时,可使用保护继承。这种继承方式在一定程度上隐藏了父类的接口,同时允许子类在内部使用这些接口进行扩展和修改。

3. 私有继承(private

  • 特点:父类的 publicprotected 成员在子类中都变为 private 成员,父类的 private 成员在子类中同样不可直接访问。
  • 示例代码
class Parent {
   
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;
};

class Child : private Parent {
   
public:
    void accessMembers() {
   
        publicVar = 10;  // 此时父类的 public 成员在子类中变为 private,可在子类成员函数中访问
        protectedVar = 20;  // 可以访问父类的 protected 成员
        // privateVar = 30;  // 错误,不能访问父类的 private 成员
    }
};

int main() {
   
    Child c;
    // c.publicVar = 5;  // 错误,在类外部不能访问 private 成员
    return 0;
}
  • 使用场景:当子类只是想复用父类的部分实现代码,而不希望继承父类的接口时,可采用私有继承。私有继承通常用于实现一些特定的内部逻辑,对外界隐藏父类的存在和相关接口。

三、派生类和基类的构造函数和析构函数执行顺序

1、构造函数执行顺序

当创建派生类的对象时,构造函数的执行顺序遵循以下规则:

1). 首先执行基类的构造函数:按照继承列表中基类出现的顺序,从最顶层的基类开始依次执行其构造函数。如果存在多个基类(例如多继承的情况),先执行在继承声明中靠前位置的基类构造函数。

2). 然后执行派生类自身的构造函数:在完成所有基类构造函数的调用后,才会执行派生类自己的构造函数,用于初始化派生类特有的成员变量等操作。

示例代码(单继承情况)

#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值