理解C++类的继承

本文讲述的是面向对象语言中类的继承的理解,文中以C++语言作为例子。

继承说明了类之间的层次结构的关系。C++中允许单继承,也允许多继承(一个类可以有多个基类)
类继承用一个冒号来表示,注意不要与作用域运算符的两个冒号相混淆。
给一个例子:

#include <iostream>
using namespace std;

class Base
{
private:
	int b_number;
public:
	Base(){}
	Base(int i) : b_number(i) { }
	int get_number() { return b_number; }
	void print() { cout << b_number << endl; }
};

class Derived : public Base
{
private:
	int d_number;
public:
	Derived(int i, int j) : Base(i), d_number(j) { };
	void print()
	{
		cout << get_number() << " ";
		cout << d_number << endl;
	}
};

int main()
{
	Base a(2);
	Derived b(3, 4);
	cout << "a is ";
	a.print();
	cout << "b is ";
	b.print();
	cout << "base part of b is ";
	b.Base::print();
	return 0;
}


例子中定义了一个基类(被继承)Class Base,Base类有一个变量,两个构造函数和两个成员函数。
Base类中有一个构造函数是这样写的。

Base(int i) : b_number(i) { }

这个函数实际上等价于:

Base(int i)
{
	b_number = i;
}

按照前一个写法,就是利用了继承的概念,Base的构造函数继承了对于b_number的初始化操作(用i对b_number赋值),在调用时也会调用其继承到的方法。
下面有一个类Derived继承了Base类。继承方式为公有继承(public继承,区别在下面讲)。由于Derived类公有继承了Base类,Derived类继承了Base类中所有的protected及public标签下的成员,可以直接调用相应的函数。当然也可以定义自己的函数。
派生类中调用基类函数的例子是,在Derived类中的print函数,调用了get_number()函数,这个函数在Derived类中并没有定义,而是在Base类中定义的。这就叫做继承。
但Derived类和Base类中都定义了一个print函数,那么在Derived类的实例中,即可以调用Derived类的print函数,也可以调用Derived类的成员函数。具体区分方法就是加上作用域限定符::。
如main函数中的两句b.print();和b.Base::print();前者没有作用域修饰符,表示调用本类的print函数,后者由作用域修饰,表示调用Base类的print函数。为什么前面调用基类的get_number()函数不会出现问题?原因就是只有一个get_number(),对于调用哪一个不会产生歧义。

继承方式分为public继承,protected继承和private继承。区别在于:

继承类型

基类中的访问权限

基类成员在派生类中的访问权限

public

公有继承

public

public

protected

protected

private

不可见

protected

保护继承

public

protected

protected

protected

private

不可见

private

私有继承

public

private

protected

private

private

不可见

在基类中的私有变量,无论什么继承方式,在派生类中都是不可见的。
最后讨论下继承的传递性:

继承类型

基类A中的访问权限

直接派生类B(继承A)中的访问权限

间接派生类C(继承B)中的访问权限

public

公有继承

public

public

public

protected

protected

protected

private

不可见

不可见

private

私有继承

public

private

不可见

protected

private

不可见

private

不可见

不可见

说明:对于构造函数继承与析构函数继承的调用顺序。对于Derived(int i, int j) : Base(i)
在调用Derived类的构造函数的时候,先执行基类Base类的构造函数,然后再执行本类的构造函数。
而析构函数的执行顺序则相反,是先执行本类的析构,再执行基类的析构。


基类与派生类的赋值关系
公有继承下:
允许的关系: 基类对象 = 派生类对象。如:
Point p; Cycle c; 且Cycle类继承Point类。
则p = c;是允许的,而c = p;是禁止的。
同样的对于指针和引用关系,也是只允许:
基类对象指针 = 派生类对象指针
基类对象引用 = 派生类对象引用

也允许用派生类对象初始化基类的对象引用,即 基类名 &基类的对象引用名 = 派生类的对象名;
当然对于类型转换,也是允许通过强制类型转换将基类指针转换为派生类指针。例:

Point p, * bp = &p;
Circle c, * dp = &c; 
dp = ( Circle * )bp;   // 显式类型转换
总结一下,基类和派生类的赋值兼容规则可归纳为如下三点:
⑴  公有派生类的对象可以赋值给基类的对象,即将公有派生类对象中从基类继承而来 的数据成员逐个赋值给基类对象的对应数据成员;
⑵  公有派生类对象的地址可以赋值给基类的对象指针;
⑶  公有派生类对象可以用来初始化基类的对象引用。
上述所有的均只在公有派生类下可用,在私有派生类下均不可用。


多继承与虚继承
多继承是只在C++中被使用,在C#和java等语言中因为太复杂和易产生二义性而被废弃。

多继承就是说同时继承多个类,如C类同时继承A类与B类。

虚继承,使用virtual关键字。例:

class Derived : virtual public Base

我们可以定义如下的继承关系:
class A
{
public:
	int a;
	A() : a(1) {};
};

class B : virtual public A {};
class C : virtual public A {};
class D : virtual public B, virtual public C {};
这样定义后,在D类中,无论将作用域限定在A,B,C还是D类中,其a均相同,即只有1个a。这样可以避免因同时从B,C继承到了A,怎样追溯而造成的混乱以及二义性。



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值