C++ 继承与派生详解:从概念到代码实战
C++ 面向对象编程:继承与派生详解
在 C++ 面向对象编程中,继承 (Inheritance) 和 派生 (Derivation) 是两个核心概念,它们让我们能够实现代码复用、提升程序的扩展性。本文将结合示例代码和讲解图片,带你深入理解继承与派生的机制和使用方法。
1. 继承与派生的基本概念
继承:保持已有类的特性而构造新类的过程。
派生:在已有类的基础上新增自己的特性而产生新类的过程。
在继承体系中:
被继承的类称为 基类 (Base Class 或 父类)。
派生出的类称为 派生类 (Derived Class 或 子类)。
继承和派生的目的:
继承的目的:实现代码重用,提高扩展性。
派生的目的:当原有程序无法解决新问题时,可以在原有类的基础上扩展功能。
2. 继承方式:public / protected / private
2.1 公有继承 (public)
特点:
基类的 public 成员在子类中保持 public。
基类的 protected 成员在子类中保持 protected。
基类的 private 成员在子类中不可直接访问。
类外访问:
只能通过子类对象访问基类的 public 成员。
代码示例:
#include <iostream>
using namespace std;#if 0
class A
{
public:void fun_A_public(){cout << "fun_A_public" << endl;}protected:void fun_A_protected(){cout << "fun_A_protected" << endl;}private:void fun_A_private(){cout << "fun_A_private" << endl;}
};// 继承方式:public、protected、private
// 1、继承方式只影响父类的成员在子类中是否需要改变访问属性,
// 也就是父类中的成员拷贝到子类中后,这些成员在子类中的其存在的访问权限
// 2、子类中是否能够访问基类中的成员,与继承方式是没有关系的。
// 子类中是否能访问基类中的成员,只与原基类中成员访问权限有关,
// 父类中的private成员子类无法访问,public、protected成员是可以直接访问。
class B : public A
{
public:void fun_B_public(){cout << "fun_B_public" << endl;// 子类中可以直接调用或访问父类中的public、protected成员,// 不需要使用类对象访问,因为继承本质就是我子类复用了基类的代码。fun_A_public();fun_B_private();// 子类中无法访问父类中的private成员// fun_A_private();fun_A_protected();}protected:void fun_B_protected(){cout << "fun_B_protected" << endl;fun_A_protected();}private:void fun_B_private(){cout << "fun_B_private" << endl;}};int main(int argc, char *argv[])
{B b;// 1、父类中的public成员,在使用public方式继承时,// 其可以通过子类对象,在类的外部直接访问。// 2、无论是什么继承方式,父类中的protected、private成员,在类外部都无法访问。b.fun_A_public();b.fun_B_public();
2.2 保护继承 (protected)
特点:
基类的 public 成员在子类中变为 protected。
基类的 protected 成员依然是 protected。
基类的 private 成员依然不可访问。
类外访问:
子类对象无法直接访问从基类继承的 public 成员,只能在子类内部访问。
2.3 私有继承 (private)
特点:
基类的 public 和 protected 成员在子类中都变为 private。
基类的 private 成员依然不可访问。
类外访问:
子类对象无法直接访问继承的成员。
3. 构造与析构顺序
在 C++ 中,当类之间存在组合或继承关系时,构造和析构的调用顺序尤为重要。
3.1 组合关系
#include <iostream>
using namespace std;class A
{
public:A();//无参构造A(int a, int b);//带参构造~A();private:int m_AA;int m_AB;
};class C
{
public:C(int a);~C();private:int m_C;
};
//当创建一个“容器”类的对象时(这里是B),会首先构造它的成员变量,然后才执行它自身的构造函数体。成员变量的构造顺序严格按照它们在类中声明的顺序,与它们在构造函数初始化列表中的顺序无关。
class B
{
public:B(int b, int c);~B();private:C ccc;A aaa; // 类组合关系int m_B;
};A::A()
{cout << "A construct" << endl;
}A::A(int a, int b) : m_AA(a), m_AB(b)
{cout << "A construct: " << m_AA << " : " << m_AB << endl;
}A::~A()
{cout << "A descstruct: " << m_AA << " : " << m_AB << endl;
}// 类组合时,当前类负责初始化构造组合类
// 原则:不仅要负责对本类中的基本类型成员数据赋初值,也要对对象成员初始化。
// 如果A类是无参构造,那么可以不用管,系统会自动调用其无参构造
// 如果我们需要使用A类的带参构造,那么B类必须负责A类的构造
B::B(int b, int c) : ccc(b), aaa(b, c), m_B(c)
{// 不允许访问组合类中的私有成员//aaa.m_AA = 11112;cout << "B construct: " << m_B << endl;
}B::~B()
{cout << "B descstruct: " << m_B << endl;
}C::C(int a) : m_C(a)
{cout << "C construct: " << m_C << endl;
}C::~C()
{cout << "C descstruct: " << m_C << endl;
}int main(int argc, char *argv[])
{// 在拥有组合关系时,当前B类中有一个A类对象的成员变量// 其构造顺序:// 1、先构造B的成员变量,但是由于成员变量中拥有A类对象// 2、所以调转至A类进行构造,先构造其成员变量// 3、在执行A类的构造函数// 4、返回B类,如果有未构造的其它成员变量,继续构造// 5、最后在执行B类构造函数B b(123, 456);return 0;
}
执行顺序:
先构造 B 的成员变量(
C
、A
)。然后执行 B 的构造函数。
析构时顺序相反:先析构 B 自身,再析构其成员。
3.2 继承关系
class A {
public:A(double val) { cout << "A construct with value: " << val << endl; }~A() { cout << "A destruct" << endl; }
};class B : public A {
public:B(double n) : A(n) { cout << "B construct" << endl; }~B() { cout << "B destruct" << endl; }
};int main() {B myB(99.5);return 0;
}
执行顺序:
先调用基类 A 的构造函数。
再执行派生类 B 的构造函数。
析构顺序相反:先析构 B,再析构 A。
4. 内部访问与外部访问
派生类可以访问基类的 public
和 protected
成员,但不能访问 private
成员。下面是派生类内部函数 show_all_internal_access
的示例:
void show_all_internal_access() {cout << "--- Inherited from A ---" << endl;fun_A_public(); // ✅ 可访问fun_A_protected(); // ✅ 可访问// fun_A_private(); // ❌ 不可访问cout << "--- B's Own Members ---" << endl;fun_B_public(); // ✅ 可访问fun_B_protected(); // ✅ 可访问fun_B_private(); // ✅ 可访问
}
5. 总结
继承和派生是 C++ 面向对象编程的基石。
public / protected / private 继承方式决定了基类成员在子类中的可见性和访问权限。
构造与析构顺序遵循 先基类 → 后子类,析构顺序相反 的原则。
子类内部能访问基类的
public
和protected
,但不能访问private
。
通过理解这些规则,我们就能写出更清晰、更易维护的 C++ 面向对象代码。