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)
请按任意键继续. . .
参考资料
参考资料来源于黑马程序员等