42.继承中的构造和析构

1.继承中的对象模型

  在C++编译器的内部可以理解为结构体,子类是由父类成员叠加子类新成员而成:

class Aclass{
public:
	int mA;
	int mB;
};
class Bclass : public Aclass{
public:
	int mC;
};
class Cclass : public Bclass{
public:
	int mD;
};
void test(){
	cout << "A size:" << sizeof(Aclass) << endl;
	cout << "B size:" << sizeof(Bclass) << endl;
	cout << "C size:" << sizeof(Cclass) << endl;
}

查看类继承的内部模型

  找到VS2013开发人员命令提示程序(一般在:C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts),打开,然后复制你工程路径,命令:cd 路径,进入你工程文件夹中(如果工程不在C盘在E盘的话,要再E:下),然后命令:cl /d1 reportSingleClassLayout类名 文件名全称

如:cl /d1 reportSingleClassLayoutSon test.cpp

2.对象构造和析构的调用原则

继承中的构造和析构

■子类对象在创建时会首先调用父类的构造函数

■父类构造函数执行完毕后,才会调用子类的构造函数

■当父类构造函数有参数时,需要在子类初始化列表(参数列表)中显示调用父类构造函数

■析构函数调用顺序和构造函数相反

class A{
public:
	A(){
		cout << "A类构造函数!" << endl;
	}
	~A(){
		cout << "A类析构函数!" << endl;
	}
};

class B : public A{
public:
	B(){
		cout << "B类构造函数!" << endl;
	}
	~B(){
		cout << "B类析构函数!" << endl;
	}
};

class C : public B{
public:
	C(){
		cout << "C类构造函数!" << endl;
	}
	~C(){
		cout << "C类析构函数!" << endl;
	}
};

void test(){
	C c;
}

输出:

A类构造函数!
B类构造函数!
C类构造函数!
C类析构函数!
B类析构函数!
A类析构函数!
请按任意键继续. . .

继承与组合混搭的构造和析构

class D{
public:
	D(){
		cout << "D类构造函数!" << endl;
	}
	~D(){
		cout << "D类析构函数!" << endl;
	}
};
class A{
public:
	A(){
		cout << "A类构造函数!" << endl;
	}
	~A(){
		cout << "A类析构函数!" << endl;
	}
};
class B : public A{
public:
	B(){
		cout << "B类构造函数!" << endl;
	}
	~B(){
		cout << "B类析构函数!" << endl;
	}
};
class C : public B{
public:
	C(){
		cout << "C类构造函数!" << endl;
	}
	~C(){
		cout << "C类析构函数!" << endl;
	}
public:
	D c;
};
void test(){
	C c;
}

输出:

A类构造函数!
B类构造函数!
D类构造函数!
C类构造函数!
C类析构函数!
D类析构函数!
B类析构函数!
A类析构函数!
请按任意键继续. . .

3. 继承中同名成员的处理方法

■当子类成员和父类成员同名时,子类依然从父类继承同名成员

■如果子类有成员和父类同名,子类访问其成员默认访问子类的成员(本作用域,就近原则)

■ 在子类通过作用域::进行同名成员区分(在派生类中使用基类的同名成员,显示使用类名限定符)

例子1:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Father
{
public:
	Father()
	{
		a = 10;
	}

	void func()
	{
		cout << "Father func" << endl;
	}
	void func(int a)
	{
		cout << "Father func(int a)" << endl;
	}

	void func(int a,int b)
	{
		cout << "Father func(int a,int b)" << endl;
	}


public:
	int a;
};
class Son :public Father
{
public:
	Son()
	{
		a = 20;
	}
	void func()
	{
		cout << "Son func" << endl;
	}
public:
	int a;
};
//当子类和父类有同名成员时,子类的同名成员会隐藏父类的同名成员
void test()
{
	Son s;
	cout << s.a << endl;
	cout << sizeof(Son) << endl;//8
	Father f;
	s.Father::a = 200;
	//通过父类名加作用域来访问
	cout<<s.Father::a << endl;
	
	cout << f.a << endl;


}
//当子类有和父类同名函数时,父类的所有函数重载都会被隐藏
void test02()
{
	Son s;
	s.func();
	//s.func(10);err
	//s.func(10, 20);err
	//通过作用域来访问隐藏的父类函数
	s.Father::func(10);
	s.Father::func(10, 20);
}
int main()
{
	test();
	system("pause");
	return EXIT_SUCCESS;
}

输出:

20
8
200
10
请按任意键继续. . .

例子2:

class Base{
public:
	Base():mParam(0){}
	void Print(){ cout << mParam << endl; }
public:
	int mParam;
};

class Derived : public Base{
public:
	Derived():mParam(10){}
	void Print(){
		//在派生类中使用和基类的同名成员,显示使用类名限定符
		cout << Base::mParam << endl;
		cout << mParam << endl;
	}
	//返回基类重名成员
	int& getBaseParam(){ return  Base::mParam; }
public:
	int mParam;
};

int main(){

	Derived derived;
	//派生类和基类成员属性重名,子类访问成员默认是子类成员
	cout << derived.mParam << endl; //10
	derived.Print();
	//类外如何获得基类重名成员属性
	derived.getBaseParam() = 100;
	cout << "Base:mParam:" << derived.getBaseParam() << endl;

	return EXIT_SUCCESS;
}

注意: 如果重新定义了基类中的重载函数,将会发生什么?

class Base{
public:
	//重载函数
	void func1(){
		cout << "Base::void func1()" << endl;
	};
	void func1(int param){
		cout << "Base::void func1(int param)" << endl;
	}
	//非重载函数
	void myfunc(){
		cout << "Base::void myfunc()" << endl;
	}
};

class Derived1 : public Base{};
class Derived2 : public Base{
public:
	void myfunc(){
		//基类myfunc被隐藏,可通过类作用域运算符指定调用基类myfunc函数
		//Base::myfunc();
		cout << "Derived2::void myfunc()" << endl;
	}
};
class Derived3 : public Base{
public:
	//改变成员函数的参数列表
	void func1(int param1, int param2){
		//Base::func1(10);  //类的内部可通过类作用域运算符访问基类重载版本的函数
		cout << "Derived3::void func1(int param1,int param2)" << endl;
	};
};
class Derived4 : public Base{
public:
	//改变成员函数的返回值
	int func1(int param){
		Base::func1(10);
		cout << "Derived4::int func1(int param)" << endl;
		return 0;
	}
};

//和基类非重载函数重名
void test01(){
	Derived1 derived1;
	derived1.myfunc();
	//和基类函数重名
	Derived2 derived2;
	derived2.myfunc();
}

//和基类重载函数重名
void test02(){

	Derived3 derived3;
	//derived3.func1();  //基类重载版本的函数fun1被全部隐藏,子类外部不可访问
	//derived3.func1(10);
	derived3.func1(10,20);

	Derived4 derived4;
	//derived4.func1(); //基类重载版本的函数fun1被全部隐藏,子类外部不可访问
	derived4.func1(10);
}

//结论:任何时候重新定义基类中的任何一个函数,子类中这种函数的任何版本都会被隐藏(非覆盖,可通过类作用域运算符调用)

4.非自动继承的函数

  不是所有的函数都能自动从基类继承到派生类中。构造函数和析构函数用来处理对象的创建和析构操作,构造和析构函数只知道对它们的特定层次的对象做什么,也就是说构造函数和析构函数不能被继承,必须为每一个特定的派生类分别创建。

  另外operator=也不能被继承,因为它完成类似构造函数的行为。也就是说尽管我们知道如何由=右边的对象如何初始化=左边的对象的所有成员,但是这个并不意味着对其派生类依然有效。

  在继承的过程中,如果没有创建这些函数,编译器会自动生成它们。

5.继承中的静态成员特性

1.静态成员可以被继承

2.继承中的静态成员变量一样会被同名的子类成员变量隐藏

3.继承中的静态成员函数中,当子类有和父类同名静态函数时,父类的所有重载静态函数都会被隐藏

4.改变从基类继承过来的静态函数的某个特征,返回值或者参数个数,将会隐藏基类重载的函数

5.静态成员函数不能是虚函数

6.从父类继承过来的静态成员变量是父类的静态成员变量

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Father0
{
public:
	static int mA;
};
class Father :public Father0
{
public:
	static void func()
	{
		cout << "Father func()" << endl;
	}
	static void func(int a)
	{
		cout << "Father func(int a)" << endl;
	}

	static void func(int a, int b)
	{
		cout << "Father func(int a,int b)" << endl;
	}
public:
	static int mA;
};
int Father::mA = 10;

class Son :public Father
{
public:
	/*static void func()
	{
		cout << "Son func()" << endl;
	}*/
	//static int func()
	//{
	//	cout << "int func" << endl;
	//	return 0;
	//}
public:
	static int mA;
};
int Son::mA = 20;
void test01()
{
	Son s;
	cout << s.mA << endl;
	s.Father::mA = 200;
	cout << Father::mA << endl;
	cout << &(s.Father::mA) << endl;
	cout << &(Father::mA) << endl;
	s.func();
	s.func(10);
	s.func(10, 20);
}
int main()
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

输出:

20
200
00007FF65257E010
00007FF65257E010
Father func()
Father func(int a)
Father func(int a,int b)
请按任意键继续. . .

参考资料

参考资料来源于黑马程序员等

posted @ 2022-10-16 21:31  CodeMagicianT  阅读(40)  评论(0编辑  收藏  举报