C++ 继承的语法
在 C++ 中,单继承是指一个派生类只从一个基类继承。下面是关于 C++ 单继承的语法、解释和代码举例:
- 语法:
class DerivedClass : access_specifier BaseClass { // 派生类的定义 };
- 其中
DerivedClass
是派生类名称,BaseClass
是基类名称,access_specifier
是访问权限修饰符,可以是public
、protected
或private
。 - 解释:
- 类之间的继承关系将基类的成员变量和成员函数(除非被隐藏或重写)继承到派生类中。
- 派生类可以直接访问基类的公有成员和保护成员(基类访问权限设置为
public
或protected
)。私有成员无法直接在派生类中访问。 - 派生类可以通过重写基类的成员函数来实现对其的定制化。
- 代码举例:
下面是一个简单的代码示例,演示了单继承的语法和不同情况的应用:
#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; }
- 在上述示例中,
DerivedClass
是从BaseClass
派生而来的派生类。派生类DerivedClass
继承了基类BaseClass
的baseFunction()
函数,并添加了自己的成员函数derivedFunction()
。
在main()
函数中,我们创建了DerivedClass
的对象dObj
。通过该对象,我们可以调用基类的成员函数baseFunction()
和派生类的成员函数derivedFunction()
。
输出结果将是:
This is a base class function This is a derived class function
- 这个示例展示了单继承的用法和效果,派生类继承了基类的功能,并且可以添加自己的新功能。
C++ 继承方式
在 C++ 中,单继承的继承方式主要包括三种访问权限:public
、protected
和 private
。这些访问权限将影响基类成员在派生类中的可见性和访问权限:
- public 继承:
- 当使用
public
继承时,基类的公有成员在派生类中仍然是公有的,保护成员在派生类中仍然是保护的。 - 派生类对象可以直接访问基类的公有和保护成员。
class Base { public: int publicMember; protected: int protectedMember; }; class Derived : public Base { // public 继承 };
- protected 继承:
- 当使用
protected
继承时,基类的公有和保护成员在派生类中均变为保护的。 - 派生类对象可以直接访问基类的保护成员,但不能访问基类的公有成员。
class Base { public: int publicMember; protected: int protectedMember; }; class Derived : protected Base { // protected 继承 };
- 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 模型和静态模型。
- vtable(虚函数表)模型:
- 在 vtable 模型中,每个类都有一个虚函数表,其中存储着该类的虚函数地址。
- 派生类通过指针访问基类的成员和虚函数表。对于基类的成员,可以直接访问;对于虚函数,则通过虚函数表的索引进行间接调用。
- 当派生类覆盖了虚函数时,将会在派生类的虚函数表中替换相应的函数地址。
- 这种模型支持多态性和运行时的动态绑定。
- 静态模型:
- 静态模型中,派生类直接继承了基类的成员,没有额外的虚函数表。
- 派生类可以通过自己的成员函数直接访问基类的成员。
- 对于虚函数,派生类如果覆盖了基类的虚函数,仍然可以使用
virtual
关键字来声明,并在派生类对象中进行覆盖。 - 这种模型不支持多态性和动态绑定,派生类中的虚函数调用始终是静态绑定。
C++ 编译器通常根据程序的具体情况选择适合的对象模型。大多数情况下,C++ 使用 vtable 模型来实现继承和多态,因为这种模型提供了更好的灵活性和运行时的决策能力。但在一些特殊情况下,比如单继承且没有虚函数时,C++ 编译器可能会选择静态模型来优化对象的存储和访问。
请注意,对象模型是由编译器和操作系统来实现的,不同的编译器和平台可能使用不同的对象模型。因此,具体的对象模型实现可能会有所不同。
C++ 继承中构造和析构的顺序
在 C++ 中,当涉及到继承关系时,派生类的构造函数和析构函数执行顺序如下:
- 构造函数的执行顺序:
- 首先,基类的构造函数会在派生类的构造函数之前被调用。
- 构造函数的调用顺序是从最基类开始,逐级向下,直到最终的派生类。
- 在派生类构造函数中,可以通过成员初始化列表指定基类的构造函数参数。
- 析构函数的执行顺序:
- 与构造函数相反,析构函数的调用顺序是从最派生类开始,逐级向上,直到最基类。
- 基类的析构函数会在派生类的析构函数执行完成后调用。
下面是一个示例说明构造函数和析构函数的执行顺序:
#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++ 中,在派生类中如果有与基类同名的成员(包括成员变量和成员函数),会根据访问权限和成员类型的不同,处理方式如下:
- 成员变量:
- 如果派生类中定义了与基类同名的成员变量,那么这个同名成员变量会隐藏基类中的同名成员变量。
- 如果需要访问被隐藏的基类同名成员变量,可以通过基类作用域解析运算符
::
来访问。
class Base { public: int num; }; class Derived : public Base { public: int num; // 隐藏了基类的 num void accessBaseNum() { Base::num = 10; // 通过作用域解析符访问基类的 num } };
- 成员函数:
- 如果派生类中定义了与基类同名的成员函数,并且函数的签名(参数列表和常量性等)与基类中的同名函数不完全匹配,则不会发生函数重载,而是隐藏基类的同名函数。
- 如果函数签名匹配,那么子类会继承基类的同名函数,并且可以通过作用域解析运算符
::
来调用基类的同名函数。
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++ 中,如果在派生类中有与基类同名的静态成员变量或静态成员函数,处理方式如下:
- 同名静态成员变量:
- 如果派生类中定义了与基类同名的静态成员变量,那么基类和派生类各自拥有自己的静态成员变量,彼此独立。即在内存中会分别存储基类的静态成员和派生类的静态成员。
class Base { public: static int num; }; int Base::num = 10; class Derived : public Base { public: static int num; }; int Derived::num = 20;
- 同名静态成员函数:
- 如果派生类中定义了与基类同名的静态成员函数,二者之间不构成覆盖(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++ 中,多继承的语法可以涉及不同的情况,包括不同的继承方式、访问权限控制和虚继承。以下是各种情况的示例:
- 多继承语法:
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; } };
- 多继承中的访问权限:派生类可以通过基类列表指定每个基类的访问权限(公有、私有或保护)。例如,在
Derived
类中,Base1
是公有继承,而Base2
是保护继承。 - 虚继承:虚继承用于解决菱形继承(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++ 中多继承的一般语法,不同的继承方式和访问权限可以根据具体的需求来选择。