欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

继承(加深)-构造函数调用顺序与函数重定义

程序员文章站 2022-05-14 09:06:21
...

一、派生类的构造函数与析构函数

        从基类派生子类时,基类的构造函数不能继承到派生类中,因此我们在定义派生类的构造函数时除了对自己的数据成员进行初始化外,还必须负责调用基类构造函数。如果派生类中有子对象,还应包含对子对象初始化的构造函数。

派生类构造函数一般的执行顺序为:

(1)最先调用基类的构造函数,多个基类则按派生类声明时列出的次序,从左到右调用,而不是初始化列表中的次序。

(2)再调用对象成员(子对象)的构造函数,按类声明中对象成员出现的次序调用,而不是初始化列表中的次序。

(3)最后执行派生类的构造函数。

下面我们看一段程序:

#include<iostream>
using namespace std;
class A
{
public:
	A()
	{
		cout << "A Constructor1" << endl;
	}
	A(int i)
	{
		x1 = i;
		cout << "A Constructor2" << endl;
	}
	void displaya()
	{
		cout << "x1=" << x1 << endl;
	}
private:
	int x1;
};

class B :public A                      //若为多继承,按从左到右的顺序调用
{
public:
	B()
	{
		cout << "B Constructor1" << endl;
	}
	B(int i)
		:A(i + 10)
	{
		x2 = i;
		cout << "B Constructor2" << endl;
	}
	void displayb()
	{
		displaya();
		cout << "x2=" << x2 << endl;
	}
private:
	int x2;
};

int main()
{
	B b(2);
	b.displayb();

	system("pause");
	return 0;
}

运行结果为:

继承(加深)-构造函数调用顺序与函数重定义

上述程序中,类B是类A的派生类,该成员初始化列表的顺序是:先是基类A的构造函数,再是派生类B中子对象的构造函数,最后是B的构造函数。

如果类中包含对象成员呢?我们看看下面的程序:

class A
{
public:
	A()
	{
		cout << "class A" << endl;
	}
};
class B
{
public:
	B()
	{
		cout << "class B" << endl;
	}
};
class C
{
	A a;
public:
	C()
	{
		cout << "class C" << endl;
	}
};
class D :public C
{
	B b;
public:
	D()
	{
		cout << "class D" << endl;
	}
};

int main()
{
	D d;

	system("pause");
	return 0;
}

运行结果如下:

继承(加深)-构造函数调用顺序与函数重定义

可以看出:

类C派生出类D,但类C包含一个类A的对象a,类D中包含一个类B的对象b,所以语句“D d”,先执行基类C中对象a的构造函数,再执行基类C的构造函数,接着是D中对象b的构造函数,最后执行类D的构造函数。

析构函数,其顺序与执行构造函数时的顺序正好相反。即次序如下:

(1)最先执行派生类的析构函数

(2)再调用对象成员(子对象)的析构函数,按类声明中对象成员出现的逆序调用,而不是初始化列表中的次序。

 (3)最后调用基类的析构函数,多个基类则按派生类声明时列出的逆序(从右到左)调用。

二、继承成员的重定义

继承成员的重定义是指重新修改继承成员函数的实现。

我们先来看一段代码:

class A
{
	int i;
public:
	void set(int x)
	{
		i = x;
	}
	void disp()
	{
		cout << "i=" << i << endl;
	}
};
class B
{
	int j;
public:
	void set(int y)
	{
		j = y;
	}
	void disp()
	{
		cout << "j=" << j << endl;
	}
};

int main()
{
	A a;
	B b;
	a.set(1);                 //调用类A的set函数
	a.disp();
	b.set(2);                 //调用类B的set函数
	b.disp();

	system("pause");
	return 0;
}

运行结果为:

继承(加深)-构造函数调用顺序与函数重定义

这是因为如果在派生类中增加一个函数原型与继承成员函数一模一样的成员函数,则该函数实现的函数体是对继承成员函数的重定义。

如果一个派生类的对象调用这个函数,首先在派生类中查找是否有该函数的定义,有则调用派生类中成员函数,否则才在所有的祖先类中查找。

三、友元关系不能继承

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。除非在子类中也声明为友元。

四、静态成员数据

基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有 一个static成员实例。

我们来看一个例子:

class Person
{
public:
	Person()
	{
		++_count;
	}
protected:
	string _name;
public:
	static int _count;
};

int Person::_count = 0;

class Student :public Person
{
protected:
	int _num;
};

void TestPerson()
{
	Person p;
	Student s1;
	Student s2;
	Student s3;

	cout << "人数:" << Person::_count << endl;
	Student::_count = 0;
	cout << "人数:" << Person::_count << endl;

}
int main()
{
	TestPerson();

	system("pause");
	return 0;
}
继承(加深)-构造函数调用顺序与函数重定义
这就可以看出来对象共用一个静态成员。