C++之类的继承与派生
一,继承与派生相关概念
- 保持已有类的特性而构造新类的过程称为继承。
- 在已有类的基础上新增自己的特性而产生新类的过程称为派生。
- 被继承的已有类称为基类(或父类)
- 派生出的新类称为派生类(或子类)
继承与派生问题举例:
二,继承与派生的目的
- 继承的目的:实现代码重用、提高扩展性。
- 派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行改造。
派生类的声明
class 派生类名 : 继承方式 基类名
{
成员声明;
};
继承方式
不同继承方式的影响主要体现在:
- 派生类成员对基类成员的访问权限
- 通过派生类对象对基类成员的访问权限
三种继承方式:
- 公有继承
- 私有继承
- 保护继承
三,公有继承(public)
类内部:
- 基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可直接访问。
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
类外部:
- 通过派生类的对象只能访问基类的public成员。
公有继承举例:
#include <iostream>
using namespace std;//父类
class A
{
public:void fun_A_public(){cout << "A::fun_A_public()" << endl;}protected:void fun_A_protected(){cout << "A::fun_A_protected()" << endl;}private:void fun_A_private(){cout << "A::fun_A_private()" << endl;}
};//子类(public继承)
class B : public A
{
public:void fun_B_public(){cout << "B::fun_B_public()" << endl;}protected:void fun_B_protected(){cout << "B::fun_B_protected()" << endl;}private:void fun_B_private(){cout << "B::fun_B_private()" << endl;}public:// 测试函数:在子类内部访问父类成员void test_in_class(){cout << "子类内部访问父类成员" << endl;fun_A_public(); //可访问(public)fun_A_protected(); //可访问(protected)// fun_A_private(); //无法访问(private)}
};int main()
{B b;cout << "--- 子类对象访问父类成员 ---" << endl;b.fun_A_public(); //可访问(public)// b.fun_A_protected(); //无法访问(protected)// b.fun_A_private(); //无法访问(private)cout << "--- 子类对象访问子类成员 ---" << endl;b.fun_B_public(); //可访问// b.fun_B_protected(); //无法访问// b.fun_B_private(); //无法访问cout << endl;b.test_in_class(); // 测试在子类内部访问父类成员return 0;
}
运行结果:
程序详解:
四,保护继承(protected)
类内部:
- 基类的public和protected成员都以protected身份出现在派生类中,但基类的private成员不可直接访问。
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
类外部:
- 通过派生类的对象不能直接访问基类中的任何成员。
保护继承举例:
#include <iostream>
using namespace std;//父类
class A
{
public:void fun_A_public(){cout << "A::fun_A_public()" << endl;}protected:void fun_A_protected(){cout << "A::fun_A_protected()" << endl;}private:void fun_A_private(){cout << "A::fun_A_private()" << endl;}public:// 测试:类A内部访问自身成员void test_in_A(){cout << "--- 类A内部访问自身成员 ---" << endl;fun_A_public(); //可访问fun_A_protected(); //可访问fun_A_private(); //可访问}
};//子类(protected继承)
class B : protected A
{
public:void fun_B_public(){cout << "B::fun_B_public()" << endl;}protected:void fun_B_protected(){cout << "B::fun_B_protected()" << endl;}private:void fun_B_private(){cout << "B::fun_B_private()" << endl;}public:// 测试:在子类内部访问父类成员void test_in_B(){cout << "--- 子类B内部访问父类A成员 ---" << endl;fun_A_public(); //可以访问(public 在 protected继承后变为 protected)fun_A_protected(); //可以访问(protected 保持为 protected)// fun_A_private(); //无法访问(private 无法继承)}
};//外部测试
int main()
{A a;B b;cout << "--- 类A对象外部访问 ---" << endl;a.fun_A_public(); //可访问(public)// a.fun_A_protected(); //无法访问// a.fun_A_private(); //无法访问cout << endl;a.test_in_A(); //类A内部测试cout << endl;b.test_in_B(); //子类内部访问测试cout << endl;cout << "--- 类B对象外部访问 ---" << endl;// b.fun_A_public(); //无法访问(public 继承变为 protected)// b.fun_A_protected(); //无法访问(protected)b.fun_B_public(); //可以访问(B自己的public)// b.fun_B_protected(); //无法访问(protected)// b.fun_B_private(); //无法访问(private)return 0;
}
运行结果:
程序详解:
五,私有继承(private)
类内部:
- 基类的public和protected成员都以private身份出现在派生类中,但基类的private成员不可直接访问。
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
类外部:
- 通过派生类的对象不能直接访问基类中的任何成员。
私有继承举例:
#include <iostream>
using namespace std;//父类
class A
{
public:void fun_A_public(){cout << "A::fun_A_public()" << endl;}protected:void fun_A_protected(){cout << "A::fun_A_protected()" << endl;}private:void fun_A_private(){cout << "A::fun_A_private()" << endl;}public:// 测试:类A内部访问自身成员void test_in_A(){cout << "--- 类A内部访问自身成员 ---" << endl;fun_A_public(); //可访问fun_A_protected(); //可访问fun_A_private(); //可访问}
};//子类(private继承)
class B : private A
{
public:void fun_B_public(){cout << "B::fun_B_public()" << endl;}protected:void fun_B_protected(){cout << "B::fun_B_protected()" << endl;}private:void fun_B_private(){cout << "B::fun_B_private()" << endl;}public:// 测试:在子类内部访问父类成员void test_in_B(){cout << "--- 子类B内部访问父类A成员 ---" << endl;fun_A_public(); //可访问(但在B中变为private)fun_A_protected(); // 可访问(但在B中变为private)// fun_A_private(); //无法访问(private成员不可继承)}
};//外部测试
int main()
{A a;B b;cout << "--- 类A对象外部访问 ---" << endl;a.fun_A_public(); //可访问(public)// a.fun_A_protected(); //无法访问// a.fun_A_private(); //无法访问cout << endl;a.test_in_A(); //类A内部测试cout << endl;b.test_in_B(); //子类内部访问测试cout << endl;cout << "--- 类B对象外部访问 ---" << endl;// b.fun_A_public(); //无法访问(在B中变为private)// b.fun_A_protected(); // 无法访问(在B中变为private)b.fun_B_public(); //可访问(B自己的public)// b.fun_B_protected(); //无法访问// b.fun_B_private(); //无法访问return 0;
}
运行结果:
程序详解:
六,继承访问总结
继承的本质是子类复用父类的代码。而继承方式 (public, protected, private) 则像一个“权限控制器”,它决定了父类的成员(主要是 public 和 protected 成员)被子类继承后,在子类中拥有什么样的访问权限。
1、 两条基本原则
子类内部访问:无论采用哪种继承方式,在子类的内部(其成员函数中),都可以直接访问父类的 public 和 protected 成员。这与继承方式无关,是继承的基本特性。
父类 private 成员的绝对性:父类的 private 成员对于子类来说永远是不可见的,子类无法以任何方式直接访问。这一点不受继承方式的影响。
2、 三种继承方式的权限变化规则
继承方式决定了父类成员在子类中的最终访问权限。这个最终权限取决于两个因素的最小值:
成员在父类中的原始权限 (public > protected > private)。
继承方式的权限 (public > protected > private)。
权限取小原则:最终权限 = min(父类成员权限, 继承方式)
下面是具体的规则和总结表:
规则:父类的成员权限保持不变地继承下来。
父类的 public 成员 -> 在子类中依然是 public。
父类的 protected 成员 -> 在子类中依然是 protected。
目的:实现 "is-a" 的关系,保持父类的公有接口在子类中仍然是公有的。
规则:将父类中更开放的 public 权限收缩为 protected。
父类的 public 成员 -> 在子类中变为 protected。
父类的 protected 成员 -> 在子类中依然是 protected。
目的:不希望父类的公有接口被外界(类外部)直接访问,但希望在后续的继承链中可以被继续继承和访问。
规则:将父类的所有可访问成员全部收缩为 private。
父类的 public 成员 -> 在子类中变为 private。
父类的 protected 成员 -> 在子类中变为 private。
目的:实现 "is-implemented-in-terms-of" 的关系,即子类仅仅是想复用父类的代码实现,而不希望父类的任何接口(无论是公有的还是保护的)对外暴露或被进一步继承。
3、 权限变化总结表
这张表清晰地展示了“权限取小”的规则:
继承方式 | 父类 public 成员 | 父类 protected 成员 | 父类 private 成员 |
public 继承 | 在子类中变为 public | 在子类中变为 protected | 在子类中不可访问 |
protected 继承 | 在子类中变为 protected | 在子类中变为 protected | 在子类中不可访问 |
private 继承 | 在子类中变为 private | 在子类中变为 private | 在子类中不可访问 |
4、 类外部访问
类外部(例如 main 函数)只能通过对象访问其 public 成员。
因此,只有在**public继承**下,父类的public成员才能通过子类对象在类外部被访问。
在 protected 和 private 继承下,父类的所有成员在子类中都变成了 protected 或 private,所以无法通过子类对象从外部访问。