【C++入门】类的继承

举报
人才程序员 发表于 2024/07/28 20:26:18 2024/07/28
【摘要】 @TOC 前言C++ 是一种功能强大的编程语言,以其面向对象编程(OOP)特性而闻名。类的继承是 C++ 面向对象编程中的一个核心概念,它允许开发人员创建新的类,这些类可以继承现有类的属性和方法。通过继承,代码可以得到复用,结构更加清晰,从而提升软件开发的效率和可维护性。本篇文章将介绍 C++ 中类的继承,包括其定义、使用方法、修饰符及多继承的概念。 继承是什么?继承是一种面向对象编程的机制...

@TOC


前言

C++ 是一种功能强大的编程语言,以其面向对象编程(OOP)特性而闻名。类的继承是 C++ 面向对象编程中的一个核心概念,它允许开发人员创建新的类,这些类可以继承现有类的属性和方法。通过继承,代码可以得到复用,结构更加清晰,从而提升软件开发的效率和可维护性。本篇文章将介绍 C++ 中类的继承,包括其定义、使用方法、修饰符及多继承的概念。


继承是什么?

继承是一种面向对象编程的机制,它允许一个类(子类或派生类)继承另一个类(基类或父类)的属性和方法。通过继承,子类可以复用基类的代码,同时还可以添加新的属性和方法,或者重写基类的方法以实现多态。

为什么需要继承?

  1. 代码重用:继承允许子类复用基类的代码,减少重复代码,提高开发效率。
  2. 提高可维护性:通过继承,可以更好地组织代码结构,使其更具层次性和可读性。
  3. 多态性:继承是实现多态的基础,多态允许一个接口多个实现,提高代码的灵活性和可扩展性。

在 C++ 中如何继承另一个类?

在 C++ 中,使用 : 符号来表示类的继承。语法如下:

class 基类 {
    // 基类的属性和方法
};

class 子类 : 访问修饰符 基类 {
    // 子类的属性和方法
};

访问修饰符

访问修饰符用于控制基类成员在子类中的可访问性。在继承时,C++ 提供了三种访问修饰符:publicprotectedprivate

  1. public 继承:基类的 publicprotected 成员在子类中保持其访问级别不变。
  2. protected 继承:基类的 publicprotected 成员在子类中都变为 protected
  3. private 继承:基类的 publicprotected 成员在子类中都变为 private
class Base {
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;
};

class PublicDerived : public Base {
    // publicVar 是 public
    // protectedVar 是 protected
    // privateVar 不可访问
};

class ProtectedDerived : protected Base {
    // publicVar 是 protected
    // protectedVar 是 protected
    // privateVar 不可访问
};

class PrivateDerived : private Base {
    // publicVar 是 private
    // protectedVar 是 private
    // privateVar 不可访问
};

类的多继承

C++ 允许一个类从多个基类继承,即多继承。多继承可以让子类继承多个基类的属性和方法,但也可能带来复杂性,如命名冲突和菱形继承问题。

class Base1 {
public:
    void show() {
        std::cout << "Base1 show" << std::endl;
    }
};

class Base2 {
public:
    void show() {
        std::cout << "Base2 show" << std::endl;
    }
};

class Derived : public Base1, public Base2 {
public:
    void show() {
        Base1::show(); // 显式调用 Base1 的 show 方法
        Base2::show(); // 显式调用 Base2 的 show 方法
    }
};

菱形继承

菱形继承是多继承的一种特殊情况,即多个派生类继承同一个基类,并且又有一个类同时继承这些派生类。这种情况可能导致基类的成员在派生类中存在多个拷贝。为了解决这个问题,可以使用虚继承。

class Base {
public:
    void show() {
        std::cout << "Base show" << std::endl;
    }
};

class Derived1 : virtual public Base {};
class Derived2 : virtual public Base {};

class Final : public Derived1, public Derived2 {
    // Base 的成员在 Final 中只有一份拷贝
};

继承中的构造函数和析构函数

在继承关系中,构造函数和析构函数的调用顺序如下:

构造函数:先调用基类的构造函数,再调用子类的构造函数。
析构函数:先调用子类的析构函数,再调用基类的析构函数。

无参数的构造函数

#include <iostream>

class Base {
public:
    Base() {
        std::cout << "Base constructor" << std::endl;
    }
    ~Base() {
        std::cout << "Base destructor" << std::endl;
    }
};

class Derived : public Base {
public:
    Derived() {
        std::cout << "Derived constructor" << std::endl;
    }
    ~Derived() {
        std::cout << "Derived destructor" << std::endl;
    }
};

int main() {
    Derived obj;
    return 0;
}

有参数的构造函数

在 C++ 中,当一个子类继承自一个带有参数的构造函数的父类(基类)时,子类必须在其构造函数的初始化列表中显式调用基类的构造函数,并传递适当的参数。这是因为基类的构造函数不会自动被调用,必须通过子类的构造函数显式指定。

以下是一个示例,展示了如何在子类的构造函数中调用带有参数的基类构造函数。

#include <iostream>

// 基类
class Base {
public:
    // 带有参数的基类构造函数
    Base(int x, int y) : a(x), b(y) {
        std::cout << "Base constructor called with a = " << a << " and b = " << b << std::endl;
    }

private:
    int a, b;
};

// 派生类
class Derived : public Base {
public:
    // 派生类构造函数,初始化列表中调用基类构造函数
    Derived(int x, int y, int z) : Base(x, y), c(z) {
        std::cout << "Derived constructor called with c = " << c << std::endl;
    }

private:
    int c;
};

int main() {
    // 创建派生类对象
    Derived obj(1, 2, 3);
    return 0;
}

代码解释

  1. 基类 Base

    • Base(int x, int y) : a(x), b(y) 是基类的构造函数,它接受两个整数参数 xy,并使用初始化列表将它们分别赋值给成员变量 ab
    • 在构造函数中打印出 ab 的值。
  2. 派生类 Derived

    • Derived(int x, int y, int z) : Base(x, y), c(z) 是派生类的构造函数,它接受三个整数参数 xyz
    • 在派生类构造函数的初始化列表中,调用基类构造函数 Base(x, y),并将 xy 传递给它。
    • 同时在初始化列表中,将参数 z 赋值给派生类的成员变量 c
    • 在构造函数中打印出 c 的值。
  3. main 函数

    • 创建 Derived 类的对象 obj,并传递参数 123。这将调用派生类的构造函数,并依次调用基类的构造函数。

输出结果

Base constructor called with a = 1 and b = 2
Derived constructor called with c = 3

继承的好处与问题

继承的好处

  1. 代码重用

    • 减少重复代码:继承允许子类复用基类的属性和方法,从而减少重复代码,提高开发效率。
    • 易于维护:当需要修改通用行为时,只需在基类中进行修改,所有继承该基类的子类都会自动继承这些更改,减少了维护成本。
  2. 提高可读性和组织性

    • 结构化代码:通过继承,可以将通用功能提取到基类中,使代码更具层次性和组织性,提高代码的可读性。
    • 逻辑清晰:继承关系可以清晰地表达类之间的关系和层次结构,使代码逻辑更加清晰。
  3. 实现多态性

    • 动态绑定:继承和虚函数结合使用,可以实现多态性,即在运行时根据对象的实际类型调用相应的方法,增强了程序的灵活性和可扩展性。
    • 统一接口:通过继承,可以为不同的子类提供统一的接口,调用者无需关心对象的具体类型,只需调用统一的接口方法即可。

继承的潜在问题

  1. 过度继承

    • 复杂性增加:过度使用继承可能会导致复杂的继承层次结构,增加代码的复杂性和理解难度。
    • 维护困难:复杂的继承关系会使代码的维护变得困难,尤其是在修改基类时,可能会影响到多个子类的行为。
  2. 紧耦合

    • 耦合度高:继承使子类和基类之间的耦合度增加,子类依赖于基类的实现,难以独立修改和扩展。
    • 修改风险:基类的修改可能会对所有子类产生影响,增加了修改代码的风险。
  3. 命名冲突

    • 方法重名:在多继承情况下,不同基类中可能存在同名的方法,导致命名冲突,增加了解决冲突的复杂性。
    • 数据成员冲突:如果基类中有同名的数据成员,也可能导致冲突,需特别小心处理。
  4. 菱形继承问题

    • 多重继承:多重继承时,若多个基类继承自同一个基类,可能会导致菱形继承问题,即子类中存在多个基类实例。
    • 数据冗余:菱形继承可能导致数据冗余和多义性问题,需使用虚继承来解决。

示例代码

以下示例展示了继承的好处和潜在问题。

#include <iostream>

// 基类
class Base {
public:
    Base(int x) : a(x) {
        std::cout << "Base constructor called with a = " << a << std::endl;
    }
    virtual void show() {
        std::cout << "Base show method" << std::endl;
    }
private:
    int a;
};

// 子类1
class Derived1 : public Base {
public:
    Derived1(int x, int y) : Base(x), b(y) {
        std::cout << "Derived1 constructor called with b = " << b << std::endl;
    }
    void show() override {
        std::cout << "Derived1 show method" << std::endl;
    }
private:
    int b;
};

// 子类2
class Derived2 : public Base {
public:
    Derived2(int x, int z) : Base(x), c(z) {
        std::cout << "Derived2 constructor called with c = " << c << std::endl;
    }
    void show() override {
        std::cout << "Derived2 show method" << std::endl;
    }
private:
    int c;
};

// 子类多继承
class MultiDerived : public Derived1, public Derived2 {
public:
    MultiDerived(int x, int y, int z) : Derived1(x, y), Derived2(x, z) {
        std::cout << "MultiDerived constructor called" << std::endl;
    }
    void show() {
        Derived1::show(); // 调用 Derived1 的 show 方法
        Derived2::show(); // 调用 Derived2 的 show 方法
    }
};

int main() {
    // 创建对象并调用方法
    Derived1 d1(1, 2);
    Derived2 d2(3, 4);
    MultiDerived md(5, 6, 7);

    d1.show();  // 调用 Derived1 的 show 方法
    d2.show();  // 调用 Derived2 的 show 方法
    md.show();  // 调用 MultiDerived 的 show 方法,分别调用 Derived1 和 Derived2 的 show 方法

    return 0;
}

总结

继承是 C++ 面向对象编程中的一个重要特性,通过继承,开发人员可以创建层次分明、结构清晰的代码,从而提高代码的复用性和可维护性。通过合理使用访问修饰符,可以控制基类成员在子类中的可访问性,多继承则提供了更大的灵活性,但也需要注意其复杂性。掌握继承及其相关概念,对于提高 C++ 编程水平和开发效率至关重要。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。