浅谈C++基类中成员函数的三种状态

我们知道,C++中的派生类继承了基类的所有数据成员以及函数:

对于基类数据成员的继承,体现在了占用内存上面。

而对于基类成员函数的继承,则不应该从占用内存的角度区去理解。应是继承对基类成员函数的调用权(子类可以调用父类的公有函数,这种行为体现了经典的面向对象编程,此子类函数或这种做法被称为Template Method,此处Method 套用了Java中的函数method)

根据是否需要在派生类中重新定义(override,覆写)基类的成员函数,可以决定基类成员函数的使用形式:

1)如果不希望derived class中重新定义基类的成员函数,则使用non-virtual函数

2)如果希望在derived class中对它进行重新定义

      a)如果基类中已经有对它的默认定义,则使用虚函数。

      b)如果基类中的函数因其抽象性而无法定义,则使用纯虚函数。此时所在的类称为抽象类。

从上面可以看出,在使用虚函数的时候,利用了类型兼容规则

类型兼容规则,是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代基类对象。所指的替代包括三种情况:

class Base{...}
class Devried:public Base{...}
Base b, *pb;
Devried d;

1)派生类的对象转化为基类的对象。

      b = d;

2)派生类的对象初始化基类对象的引用。

      Base &rb = d;

3)派生类对象的地址转换为指向基类的指针。

       pb = &d;

类型兼容规则的引入,使得基类及其公有派生类的对象,可以使用相同的函数对它们统一进行处理。因为当函数的形参为基类的对象(或引用、指针)时,实参可以是派生类的对象(或指针),而没有必要为每一个类设计一个单独的模块,大大提高了程序的效率。可以说,类型兼容规则是多态性的重要基础之一。

下面是一般虚函数的使用:

#include
using namespace std;

class Base1
{
public:
	virtual void display() const;
}; 
void Base1::display()const
{
	cout<<"Base1::display()"<display();	//只有通过基类的指针或引用调用虚函数时,才会发生动态绑定 
} 
int main()
{
	Base1 base1;
	Base2 base2;
	Derived derived;
	f(&base1);
	f(&base2);
	f(&derived);
    return 0;
}

输出结果如下:


一般虚函数中还有虚析构函数:

#include
using namespace std;
class A
{
public:
	~A();
}; 
A::~A()
{
	cout<<"A destructor"<

纯虚函数:

#include
using namespace std;

class Base1	//带有纯虚函数的类是抽象类 
{
public:
	virtual void display() const=0;	//纯虚函数,不需要给出定义,为整个类族提供了通用的外部接口语义,派生类中再对同名函数进行具体实现 
}; 

class Base2:public Base1
{
public:
	void display() const;	//覆盖基类的虚函数 
}; 
void Base2::display()const
{
	cout<<"Base2::display()"<display();	//只有通过基类的指针或引用调用虚函数时,才会发生动态绑定,注意 "->" 
} 
int main()
{
	Base2 base2;
	Derived derived;
	f(&base2);
	f(&derived);
    return 0;
}

输出结果为:


以上均体现了C++中类的多态性,这也是C++语言常盛不衰的经典之处。

既然提到了“虚”,那就By the way一下虚基类:(不体现多态性)

#include
using namespace std;
class Base0
{
public:
	int var0;
	void fun0()
	{
		cout<<"Member of Base0"<

程序输出:

Member of Base0
所以,被声明为虚基类的Base0的成员var0和fun0只有一份副本,而通过两条派生路径继承,更加节省了内存空间。

以上类族的相关类成员的UML图形表示如下:


浅谈C++基类中成员函数的三种状态_第1张图片

如果没有声明虚基类,则是:

浅谈C++基类中成员函数的三种状态_第2张图片

此时,若通过Derived的对象去访问fun0和var0(要避免造成二义性),则是:

main()
{
	Derived d;
	d.Base1::var0=1;
	d.Base1::fun0();
	d.Base2::var0=2;
	d.Base2::fun0();	
}


你可能感兴趣的:(c++学习笔记)