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

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。

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

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

时间: 2024-08-25 06:34:01

函数和数据的继承及访问权限的相关文章

this/super/static/final/匿名对象/继承/抽象类/访问权限修饰符

1.this关键字的作用     1)调用本类中的属性;     2)调用本类中的构造方法;且只能放首行,且必须留一个构造方法作为出口,即不能递归调用     3)表示当前对象; 2.匿名对象     直接new出来的对象,不创建变量名,且只能使用一次,通常作为方法的参数及返回值使用 3.继承     继承是面向对象的三大特征之一,通过关键字:extends实现,被继承的类称为父类(也称作基类/超类),实现继承的类称为子类(也称作派生类).     特点:     (1)通过继承,子类可以直接访

java软件包的访问权限和继承

public:公共权限,可以修饰类.成员变量和成员函数,不论是否在同一个包中均可自由访问 package wang; //当一个类的权限为public时,类名必须和文件名相同 public class Person{ public String name; //public权限 public void introduce(){ System.out.println(name); } } package chavez; class Test{ public static void main(Str

关于C++类中访问权限的若干疑问(虚函数访问权限)

下面这样一个程序:(以下程序都是在VS2010下实现) 1: class Base 2: { 3: public: 4: virtual void func() 5: { 6: cout<<"Base virtual func"<<endl; 7: } 8: }; 9: 10: class Derived: public Base 11: { 12: private: 13: virtual void func() 14: { 15: cout<<&

C++之类成员的访问权限详解(一)

概念解析 众所周知,面向对象编程语言的特征之一就是封装,不同编程语言对于封装提供的实现有所不同,但原理大体上是相同的.C++提供了三种不同程度的访问权限来实现,主要是通过public.private.protected三个关键字实现的.下面我们来详细分析一下这三个关键字的具体作用和区别. 这三个关键字主要用来控制类或者结构体成员变量和成员函数的访问权限,称为成员访问限定符,分别代表公有的.受保护的.私有的,它们所表达的封装程度不同,在使用时需要特别注意. 访问权限限定符的使用又跟类本身的性质有关

静态变量和静态方法的访问权限

*  ----------------静态变量的访问权限 *                              public     private      protected     default *  当前包同一个类              可以      可以        可以        可以 * *  当前包里面别的类            可以      不可以      可以        可以 * *  别的包里别的类              可以      

成员变量和成员方法的访问权限

*  ----------------成员变量的访问权限 *                              public     private       protected    default *  当前包同一个类              可以      可以        可以        可以 * *  当前包里面别的类            可以      不可以      可以        可以 * *  别的包里别的类              可以      

C++继承后的虚函数访问权限

今天在写代码时发现对继承后的函数访问权限不太清楚,于是自己做了个测试: 1.头文件(test.h) 1 #include <iostream> 2 using namespace std; 3 4 class A{ 5 private: 6 void print(){ 7 cout << "this is A" << endl; 8 } 9 }; 10 11 class B:public A{ }; A为基类,B为A的子类. 2.源文件(test.c

C++虚函数访问权限的改变

如果在基类中虚函数的访问权限是一种情况,那么派生类在继承基类的时候,派生类可以重新定义基类虚函数的访问权限,经过实例验证是正确的. 从这里也说明了函数的覆盖或者说重定义跟前面的访问权限修饰没多大关系 //Base.h #pragma once #include <iostream> using namespace std; class Base { public: Base(void){} ~Base(void){} virtual void fun(){cout<<"T

C++语言笔记系列之十四——继承后的访问权限

1.析构函数不继承:派生类对象在析构时,基类析构函数的调用顺序与构造函数相反. 注:派生类对象建立时要调用基类构造函数,派生类对象删除时要调用基类析构,顺序与构造函数严格相反. 2.例子 example 1 #include <iostream.h> #include <math.h> class Point { public: Point(double a, double b, doule c) { x = a; y = b; z = c; } double Getx() {re