首页 > 代码库 > 函数和数据的继承及访问权限

函数和数据的继承及访问权限

C++通过类派生(Class Derivation)的机制支持继承(Inheritance)。允许程序员在保持原有类特性的基础上进行扩展,增加功能,派生出新类。

继承的方式有以下2种:单一继承和多重继承。

派生类的定义中包括子类新增加的成员和继承父类需要重写的成员。C++允许在派生类中重新声明和定义这些成员函数,使这些函数具有新的功能,称之为重写或覆盖。重写函数起屏蔽、更新作用,取代基类成员,完成新功能。

测试一:子类继承基类的所有属性和函数,设基类函数和数据均为public类型,并且以public方式被继承,做如下测试。例如:

// test.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include <iostream>    
#include <string>    
using namespace std;    

class CBox
{
public:
	CBox()
	{
		cout<<"调用CBox类的构造函数;"<<endl;
	}

	void SetLength(double len)  //设置盒子的长
	{
		cout<<"CBox类中设置盒子的长;"<<endl;
		length = len;
	}
	void SetWidth(double w)  //设置盒子的宽
	{
		cout<<"CBox类中设置盒子的宽;"<<endl;
		width = w;
	}
	void SetHeight(double h)  //设置盒子的高
	{
		cout<<"CBox类中设置盒子的高;"<<endl;
		heigth = h;
	}

	void ShowBox() //显示盒子长、宽、高的成员函数
	{
		cout<<"CBox类中显示盒子数据;"<<endl;
		cout<<"length="<<length<<endl
			<<"width="<<width<<endl
			<<"heigth="<<heigth<<endl;
	}

	~CBox()
	{
		cout<<"调用CBox类的析构函数;"<<endl;
	}
public:
	double length,width,heigth;
};

class CColorbox:public CBox
{
public:
	CColorbox()
	{
		cout<<"调用CColorbox类的构造函数;"<<endl;
	}

	~CColorbox()
	{
		cout<<"调用CColorbox类的析构函数;"<<endl;
	}
};

void test()
{
	CColorbox colorbox;
	colorbox.SetHeight(10);
	colorbox.SetWidth(5);
	colorbox.SetLength(20);

	colorbox.ShowBox();
}

int _tmain(int argc, _TCHAR* argv[])
{
	test();
	system("pause");
	return 0;
}
运行结果:

首先调用基类的构造函数,再调用子类构造函数。析构时先析构子类后析构基类。子类继承了基类的所有公有函数和成员,除了基类的构造、析构函数。


测试二:子类重写基类的函数

子类中重写基类的函数后,默认调用的是子类的函数!!!

1、子类中添加基类中同名但不同参数的函数,则基类的带参数的Setheight(double h)函数首先被调用!!!

则编译出错test.cpp(72): error C2660: “CColorbox::SetHeight”: 函数不接受 1 个参数:

class CColorbox:public CBox
{
public:
	CColorbox()
	{
		cout<<"调用CColorbox类的构造函数;"<<endl;
	}
	
	void SetHeight()   //新添加的
	{
		cout<<"重载:调用CColorbox类的与基类同名但不同参数的SetHeight函数;"<<endl;
	}

	~CColorbox()
	{
		cout<<"调用CColorbox类的析构函数;"<<endl;
	}
};

void test()
{
	CColorbox colorbox;
	colorbox.SetHeight(10);
	colorbox.SetHeight();  //新添加的
	colorbox.SetWidth(5);
	colorbox.SetLength(20);

	colorbox.ShowBox();
}


2、如果有需要在子类中对基类的带参数的Setheight(double h)函数进行重载,则必须首先在子类中添加同名、同参数的Setheight(double h)函数覆盖掉基类的函数

,才能够对其(子类继承的函数)进行重载。

class CColorbox:public CBox
{
public:
	CColorbox()
	{
		cout<<"调用CColorbox类的构造函数;"<<endl;
	}
	void SetHeight(double h)  //新添加。删除此函数,则子类中就不存在SetHeight()
	{
		cout<<"调用CColorbox类的与基类同名同参数的SetHeight函数;"<<endl;
		heigth = h;
	}
	void SetHeight()
	{
		cout<<"重载:调用CColorbox类的与基类同名但不同参数的SetHeight函数;"<<endl;
	}

	~CColorbox()
	{
		cout<<"调用CColorbox类的析构函数;"<<endl;
	}
};
调用结果:子类中重写基类的函数后,默认调用的是子类的函数!!!



3、在子类中添加新的函数和数据成员,调用子类新功能:

class CColorbox:public CBox
{
public:
	CColorbox()
	{
		cout<<"调用CColorbox类的构造函数;"<<endl;
	}
	void SetHeight(double h)  //删除此函数,则子类中就不存在SetHeight()
	{
		cout<<"调用CColorbox类的与基类同名同参数的SetHeight函数;"<<endl;
		heigth = h;
	}
	void SetHeight()
	{
		cout<<"重载:调用CColorbox类的与基类同名但不同参数的SetHeight函数;"<<endl;
	}

	void SetColor(int c)  
	{
		cout<<"CColorbox类中设置盒子的颜色;"<<endl;
		color = c;
	}
	void ShowColBox()
	{
		cout<<"调用CColorbox类中ShowColBox();"<<endl;
		cout<<"color="<<color<<endl;  //访问自己的私有成员
		cout<<"length"<<length<<endl;
	}
	~CColorbox()
	{
		cout<<"调用CColorbox类的析构函数;"<<endl;
	}
public:
	int color;
};

void test()
{
	CColorbox colorbox;
	colorbox.SetHeight(10);
	colorbox.SetHeight();
	colorbox.SetWidth(5);
	colorbox.SetLength(20);
	colorbox.SetColor(1);

	colorbox.ShowBox();
	colorbox.ShowColBox();
}
运行结果:


三、数据成员的重载

因为成员重载后,基类和子类中各有一份相同的数据成员,调用那一个成员就成了一个需要讨论的问题,测试时采用对于类成员的显式调用完成显示。

实验结果证明:基类函数会调用基类的数据成员,子类的成员函数默认调用子类的数据成员!!!

class CColorbox:public CBox
{
public:
	CColorbox()
	{
		cout<<"调用CColorbox类的构造函数;"<<endl;
	}

	void SetColLength(double len)   //子类设置长度函数
	{
		cout<<"CColorbox类中设置盒子的长;"<<endl;
		length = len;
	}

	void ShowColBox() //显示盒子长、宽、高的成员函数
	{
		cout<<"显示基类长度;"<<endl;
		cout<<"length = "<<CBox::length<<endl;
		cout<<"显示子类长度;"<<endl;
		cout<<"length = "<<CColorbox::length<<endl;
	}

	~CColorbox()
	{
		cout<<"调用CColorbox类的析构函数;"<<endl;
	}
public:
	int length;  //添加同名成员变量
};

void test()
{
	CColorbox colorbox;
	colorbox.SetLength(20);   //调用基类函数设置长度
	colorbox.SetColLength(10);  //调用子类函数设置长度

	colorbox.ShowColBox();
}
显示运行结果:


1、若将test中调用基类的赋值函数注释掉,则显示基类长度中,成员变量为随机值;

2、若要调用基类的长度成员,则要进行显式引用,即CBox::length。

派生类的生成

仔细分析派生新类这个过程,实际是经历了以下步骤:

第一步是继承基类的成员,不论是数据成员,还是成员函数,除构造函数与析构函数外全部接收(即全部拷贝,虽然子类中覆盖后只是显示调用,但仍可调用),全部成为派生类的成员。

子类中访问基类成员的拷贝方式为:子类对象.基类名::基类成员。如:

colorbox.CBox::length。

第二步是重写基类成员。如果派生类声明了一个与基类成员函数相同的成员函数时,派生类中的新成员则屏蔽了基类同名成员,类似函数中的局部变量屏蔽全局变量。称为同名覆盖(Override)。

由code1分析:

1、子类中重写了基类中的同名函数,则基类的同名函数不会再出现在子类。

1)重写:void SetHeight(double h);或void SetHeight();则基类的void SetHeight(double h)函数将不能在子类中显式调用。 2)若子类中需要重载SetHeight函数,则需要先重写与基类同参、同名、同返回值的函数,再进行重载。

2、子类中重写了基类中的同名数据,则该数据成员在子类中也存在了一份拷贝,如 length,则,默认方式是:继承过来的基类函数调用基类中的拷贝length,若用子类函数则调用子类中的length。

第三步是定义新成员。新成员必须与基类成员不同名,是派生类自己的新特性。派生类新成员的加入使得派生类在功能上有所发展。这一步是继承与派生的核心特征。

第四步是重写构造函数与析构函数。因为派生类不继承基类的构造函数与析构函数,并且派生类的需要对新添加的数据成员进行必要的初始化,所以构造函数与析构函数需要重写。



函数和数据的继承及访问权限