如果父类成员和子类成员名字相同是否允许?会发生什么?
同名覆盖
#include<iostream>
using namespace std;
class Base
{
public:
int m_data;
Base():m_data(1)
{ }
};
class Derived : public Base
{
public:
int m_data;
Derived():m_data(2)
{ }
};
int main()
{
Derived d;
cout<<"base data: "<<d.Base::m_data<<endl;
cout<<"derived data: "<<d.m_data<<endl;
cout<<sizeof(d)<<endl;
return 0;
}
运行结果
base data: 1
derived data: 2
8
说明父类和子类是允许存在同名成员的,只不过父类的成员被编译器隐藏了,正常访问得到的是子类成员的值。
那么如果是函数同名呢?
函数重写
函数重写是同名覆盖的一种特殊情况,即子类中重新实现父类中的同名函数,属于同名覆盖
#include<iostream>
using namespace std;
class Base
{
public:
void print()
{
cout<<"Base class"<<endl;
}
};
class Derived : public Base
{
public:
void print()
{
cout<<"Derived class"<<endl;
}
};
int main()
{
Derived d;
d.Base::print();
d.print();
return 0;
}
运行结果
Base class
Derived class
如果调用how_to_print 函数,期望传入父类对象时调用父类打印函数,传入子类对象时调用子类对象函数
void how_to_print(Base* p)
{
p->print();
}
int main()
{
Base b;
Derived d;
how_to_print(&b);
how_to_print(&d);
return 0;
}
结果却是都打印Base class
Base class
Base class
结果没有符合预期,分析:
- 传入父类对象b的地址时,父类指针指向父类对象,打印正常;
- 传入子类对象d的地址时,父类指针指向子类对象,此时由于赋值兼容性(子类对象可以当作父类对象使用),子类对象退化为父类对象(父类指针只能访问父类成员),编译器认为父类指针指向的是父类对象,因此最终调用了父类的打印函数
以上结果是合理的,却没有符合预期的目的,这也是函数重写带来的问题。如果不能实现以上目的,函数重写是没有意义的,那么如何实现父类指针(引用)指向:
实际上以上行为就是多态
多态
所谓多态,即同样的调用语句,在实际运行时存在不同的表现状态,依据则是对象的类型不同
要实现上文中函数重写的多态,需要引入virtual 关键字,C++原生支持多态
-
通过使用virtual关键字对多态进行支持 -
被virtual声明的函数被重写后具有多态特性 -
被virtual声明的函数叫做虚函数
在父类函数print 声明前添加virtual 关键字,print 函数成为虚函数,子类重写的函数也将自动变成虚函数,这样就可以实现多态
class Base
{
public:
virtual void print()
{
cout<<"Base class"<<endl;
}
};
运行结果
Base class
Derived class
多态的意义:
- 在程序运行过程中展现出动态的特性
- 函数重写必须多态实现,否则没有意义
- 多态是面向对象组件化程序设计的基础特性
总结
- 同名覆盖是继承时发生在父类和子类之间,子类同名成员覆盖(屏蔽)父类同名成员的现象;
- 函数重写也是同名覆盖,函数重写多态实现才有意义,C++通过
virtual 支持多态 - 多态是面向对象组件化程序设计的基础特性
参考
狄泰软件学院 - C++深度解析教程 - 第47课、48课、49课
|