C++学习之类的继承

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: C++学习之类的继承

C++ 继承的语法

在 C++ 中,单继承是指一个派生类只从一个基类继承。下面是关于 C++ 单继承的语法、解释和代码举例:

  1. 语法
class DerivedClass : access_specifier BaseClass {
    // 派生类的定义
};
  1. 其中 DerivedClass 是派生类名称,BaseClass 是基类名称,access_specifier 是访问权限修饰符,可以是 publicprotectedprivate
  2. 解释
  • 类之间的继承关系将基类的成员变量和成员函数(除非被隐藏或重写)继承到派生类中。
  • 派生类可以直接访问基类的公有成员和保护成员(基类访问权限设置为 publicprotected)。私有成员无法直接在派生类中访问。
  • 派生类可以通过重写基类的成员函数来实现对其的定制化。
  1. 代码举例
    下面是一个简单的代码示例,演示了单继承的语法和不同情况的应用:
#include <iostream>
// 基类定义
class BaseClass {
public:
    void baseFunction() {
        std::cout << "This is a base class function" << std::endl;
    }
};
// 派生类定义
class DerivedClass : public BaseClass {
public:
    void derivedFunction() {
        std::cout << "This is a derived class function" << std::endl;
    }
};
int main() {
    // 创建派生类对象
    DerivedClass dObj;
    // 调用基类函数
    dObj.baseFunction();
    // 调用派生类函数
    dObj.derivedFunction();
    return 0;
}
  1. 在上述示例中,DerivedClass 是从 BaseClass 派生而来的派生类。派生类 DerivedClass 继承了基类 BaseClassbaseFunction() 函数,并添加了自己的成员函数 derivedFunction()
    main() 函数中,我们创建了 DerivedClass 的对象 dObj。通过该对象,我们可以调用基类的成员函数 baseFunction() 和派生类的成员函数 derivedFunction()
    输出结果将是:
This is a base class function
This is a derived class function
  1. 这个示例展示了单继承的用法和效果,派生类继承了基类的功能,并且可以添加自己的新功能。

C++ 继承方式

在 C++ 中,单继承的继承方式主要包括三种访问权限:publicprotectedprivate。这些访问权限将影响基类成员在派生类中的可见性和访问权限:

  1. public 继承
  • 当使用 public 继承时,基类的公有成员在派生类中仍然是公有的,保护成员在派生类中仍然是保护的。
  • 派生类对象可以直接访问基类的公有和保护成员。
class Base {
public:
    int publicMember;
protected:
    int protectedMember;
};
class Derived : public Base {
// public 继承
};
  1. protected 继承
  • 当使用 protected 继承时,基类的公有和保护成员在派生类中均变为保护的。
  • 派生类对象可以直接访问基类的保护成员,但不能访问基类的公有成员。
class Base {
public:
    int publicMember;
protected:
    int protectedMember;
};
class Derived : protected Base {
// protected 继承
};
  1. private 继承
  • 当使用 private 继承时,基类的公有和保护成员在派生类中均变为私有的。
  • 派生类对象不能直接访问基类的任何成员,除非通过派生类内部的成员函数实现。
class Base {
public:
    int publicMember;
protected:
    int protectedMember;
};
class Derived : private Base {
// private 继承
};

下面是一个带有继承方式的代码示例:

#include <iostream>
class Base {
public:
    int publicMember;
protected:
    int protectedMember;
private:
    int privateMember;
};
class DerivedPublic : public Base {
public:
    void accessBaseMembers() {
        publicMember = 1; // 可以访问公有成员
        protectedMember = 2; // 可以访问保护成员
        // privateMember = 3; // 无法访问私有成员
    }
};
class DerivedProtected : protected Base {
public:
    void accessBaseMembers() {
        publicMember = 1; // 可以访问保护成员(在 DerivedProtected 内可视为公有)
        protectedMember = 2; // 可以访问保护成员
        // privateMember = 3; // 无法访问私有成员
    }
};
class DerivedPrivate : private Base {
public:
    void accessBaseMembers() {
        // publicMember = 1; // 无法访问任何基类成员
        // protectedMember = 2;
        // privateMember = 3;
    }
};
int main() {
    DerivedPublic dPub;
    dPub.publicMember = 10; // 可以访问基类公有成员
    DerivedProtected dPro;
    // dPro.publicMember = 20;  // 无法访问基类公有成员
    dPro.accessBaseMembers(); 
    DerivedPrivate dPri;
    // dPri.publicMember = 30; // 无法访问基类公有成员
    return 0;
}

在上面的示例中,DerivedPublic 使用了 public 继承,DerivedProtected 使用了 protected 继承,DerivedPrivate 使用了 private 继承。根据不同的继承方式,派生类对于基类成员的访问权限不同。

C++ 继承中的对象模型

在 C++ 的继承中,对象模型描述了派生类是如何继承和存储基类的成员的。C++ 中的对象模型主要有两种:vtable 模型和静态模型。

  1. vtable(虚函数表)模型
  • 在 vtable 模型中,每个类都有一个虚函数表,其中存储着该类的虚函数地址。
  • 派生类通过指针访问基类的成员和虚函数表。对于基类的成员,可以直接访问;对于虚函数,则通过虚函数表的索引进行间接调用。
  • 当派生类覆盖了虚函数时,将会在派生类的虚函数表中替换相应的函数地址。
  • 这种模型支持多态性和运行时的动态绑定。
  1. 静态模型
  • 静态模型中,派生类直接继承了基类的成员,没有额外的虚函数表。
  • 派生类可以通过自己的成员函数直接访问基类的成员。
  • 对于虚函数,派生类如果覆盖了基类的虚函数,仍然可以使用 virtual 关键字来声明,并在派生类对象中进行覆盖。
  • 这种模型不支持多态性和动态绑定,派生类中的虚函数调用始终是静态绑定。

C++ 编译器通常根据程序的具体情况选择适合的对象模型。大多数情况下,C++ 使用 vtable 模型来实现继承和多态,因为这种模型提供了更好的灵活性和运行时的决策能力。但在一些特殊情况下,比如单继承且没有虚函数时,C++ 编译器可能会选择静态模型来优化对象的存储和访问。

请注意,对象模型是由编译器和操作系统来实现的,不同的编译器和平台可能使用不同的对象模型。因此,具体的对象模型实现可能会有所不同。

C++ 继承中构造和析构的顺序

在 C++ 中,当涉及到继承关系时,派生类的构造函数和析构函数执行顺序如下:

  1. 构造函数的执行顺序
  • 首先,基类的构造函数会在派生类的构造函数之前被调用。
  • 构造函数的调用顺序是从最基类开始,逐级向下,直到最终的派生类。
  • 在派生类构造函数中,可以通过成员初始化列表指定基类的构造函数参数。
  1. 析构函数的执行顺序
  • 与构造函数相反,析构函数的调用顺序是从最派生类开始,逐级向上,直到最基类。
  • 基类的析构函数会在派生类的析构函数执行完成后调用。

下面是一个示例说明构造函数和析构函数的执行顺序:

#include <iostream>
class Base {
public:
    Base() { std::cout << "Base constructed" << std::endl; }
    ~Base() { std::cout << "Base destroyed" << std::endl; }
};
class Derived : public Base {
public:
    Derived() { std::cout << "Derived constructed" << std::endl; }
    ~Derived() { std::cout << "Derived destroyed" << std::endl; }
};
int main() {
    Derived d;
    return 0;
}

以上代码的执行输出顺序为:

Base constructed
Derived constructed
Derived destroyed
Base destroye

可以看出,构造函数按照类的层次结构自顶向下执行,而析构函数则按照相反的顺序执行,确保正确的资源释放顺序。这种顺序确保了基类的构造和析构在派生类的构造和析构之前和之后进行,避免了可能的资源管理问题。

C++ 同名成员处理

在 C++ 中,在派生类中如果有与基类同名的成员(包括成员变量和成员函数),会根据访问权限和成员类型的不同,处理方式如下:

  1. 成员变量
  • 如果派生类中定义了与基类同名的成员变量,那么这个同名成员变量会隐藏基类中的同名成员变量。
  • 如果需要访问被隐藏的基类同名成员变量,可以通过基类作用域解析运算符 :: 来访问。
class Base {
public:
    int num;
};
class Derived : public Base {
public:
    int num; // 隐藏了基类的 num
    void accessBaseNum() {
        Base::num = 10; // 通过作用域解析符访问基类的 num
    }
};
  1. 成员函数
  • 如果派生类中定义了与基类同名的成员函数,并且函数的签名(参数列表和常量性等)与基类中的同名函数不完全匹配,则不会发生函数重载,而是隐藏基类的同名函数。
  • 如果函数签名匹配,那么子类会继承基类的同名函数,并且可以通过作用域解析运算符 :: 来调用基类的同名函数。
class Base {
public:
    void display() {
        std::cout << "Base display" << std::endl;
    }
};
class Derived : public Base {
public:
    void display() {
        std::cout << "Derived display" << std::endl;
    }
    void callBaseDisplay() {
        Base::display(); // 调用基类的 display 函数
    }
};

同名成员在派生类中的处理方式取决于成员变量还是成员函数,是否覆盖或隐藏基类的同名成员,以及是否需要通过作用域解析符来访问基类的同名成员。开发人员应注意理解同名成员的处理规则,避免出现意外情况。

C++ 同名静态成员处理

在 C++ 中,如果在派生类中有与基类同名的静态成员变量或静态成员函数,处理方式如下:

  1. 同名静态成员变量
  • 如果派生类中定义了与基类同名的静态成员变量,那么基类和派生类各自拥有自己的静态成员变量,彼此独立。即在内存中会分别存储基类的静态成员和派生类的静态成员。
class Base {
public:
    static int num;
};
int Base::num = 10;
class Derived : public Base {
public:
    static int num;
};
int Derived::num = 20;
  1. 同名静态成员函数
  • 如果派生类中定义了与基类同名的静态成员函数,二者之间不构成覆盖(override)关系,而是属于隐藏关系(hide)。因此,调用静态成员函数时,会根据所使用的类型来决定调用哪个版本的函数。
class Base {
public:
    static void display() {
        std::cout << "Base display" << std::endl;
    }
};
class Derived : public Base {
public:
    static void display() {
        std::cout << "Derived display" << std::endl;
    }
};

在使用静态成员时,需要注意静态成员在不同类中的作用域,以及使用作用域解析运算符 :: 来访问特定类中的静态成员。静态成员变量是每个类的独立实体,而静态成员函数则依赖于类型来解析调用。

C++ 多继承的语法

在 C++ 中,多继承的语法可以涉及不同的情况,包括不同的继承方式、访问权限控制和虚继承。以下是各种情况的示例:

  1. 多继承语法
class Base1 {
public:
    void func1() {
        std::cout << "Function 1 from Base1" << std::endl;
    }
};
class Base2 {
public:
    void func2() {
        std::cout << "Function 2 from Base2" << std::endl;
    }
};
class Derived : public Base1, protected Base2 {
public:
    void func3() {
        std::cout << "Function 3 from Derived" << std::endl;
    }
};
  1. 多继承中的访问权限:派生类可以通过基类列表指定每个基类的访问权限(公有、私有或保护)。例如,在 Derived 类中,Base1 是公有继承,而 Base2 是保护继承。
  2. 虚继承:虚继承用于解决菱形继承(Diamond Inheritance)问题,即两个类继承同一个基类,然后又被一个第三个类同时继承。在虚继承中,基类的子对象只被创建一次,以解决二义性问题。
class A {
public:
    int a;
};
class B : virtual public A {
public:
    int b;
};
class C : virtual public A {
public:
    int c;
};
class D : public B, public C {
public:
    int d;
}

以上是 C++ 中多继承的一般语法,不同的继承方式和访问权限可以根据具体的需求来选择。

关注我,不迷路,共学习,同进步

关注我,不迷路,共学习,同进步

相关文章
|
11天前
|
算法 网络安全 区块链
2023/11/10学习记录-C/C++对称分组加密DES
本文介绍了对称分组加密的常见算法(如DES、3DES、AES和国密SM4)及其应用场景,包括文件和视频加密、比特币私钥加密、消息和配置项加密及SSL通信加密。文章还详细展示了如何使用异或实现一个简易的对称加密算法,并通过示例代码演示了DES算法在ECB和CBC模式下的加密和解密过程,以及如何封装DES实现CBC和ECB的PKCS7Padding分块填充。
33 4
2023/11/10学习记录-C/C++对称分组加密DES
|
1月前
|
编译器 C++ 开发者
【C++】继承
C++中的继承是面向对象编程的核心特性之一,允许派生类继承基类的属性和方法,实现代码复用和类的层次结构。继承有三种类型:公有、私有和受保护继承,每种类型决定了派生类如何访问基类成员。此外,继承还涉及构造函数、析构函数、拷贝构造函数和赋值运算符的调用规则,以及解决多继承带来的二义性和数据冗余问题的虚拟继承。在设计类时,应谨慎选择继承和组合,以降低耦合度并提高代码的可维护性。
33 1
【C++】继承
|
4月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
92 0
|
2月前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
2月前
|
安全 程序员 编译器
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
92 11
|
2月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
67 1
|
2月前
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
50 1
|
2月前
|
Java 编译器 C++
c++学习,和友元函数
本文讨论了C++中的友元函数、继承规则、运算符重载以及内存管理的重要性,并提到了指针在C++中的强大功能和使用时需要注意的问题。
30 1
|
2月前
|
安全 编译器 程序员
C++的忠实粉丝-继承的热情(1)
C++的忠实粉丝-继承的热情(1)
22 0
|
2月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
41 0