c++的多态
来源:互联网 发布:java获取tomcat的端口 编辑:程序博客网 时间:2024/05/29 03:10
1. 子类父类中同名成员的冲突
问:子类中是否可以定义父类中的同名成员变量?
class BaseCls{public: int a; void BasePrint() { std::cout << "Base::a = " << a << std::endl; } };class SubCls : public BaseCls{public: int a; void SubPrint() { std::cout << "SubCls::a = " << a << std::endl; }};int main(void){ SubCls s1; s1.a = 10; //为基类的a赋值还是为子类的a赋值 s1.SubPrint(); s1.BasePrint(); return 0;}
编译运行:
可见,”s1.a = 10;”是为子类中的成员变量a赋值。子类定义的与父类同名的成员变量将会隐藏父类中的同名成员,但是注意,仅仅是隐藏而非销毁,父类中的同名成员依旧存在于子类中,可以通过作用:(作用域分辨符)访问父类中的同名成员。
int main(void){ SubCls s1; s1.a = 10; //为子类的a赋值 s1.BaseCls::a = 100; //为基类的a赋值 s1.SubPrint(); s1.BasePrint(); std::cout << "&s1.a = " << &s1.a << std::endl; std::cout << "&s1.BaseCls::a = " << &s1.BaseCls::a << std::endl; return 0;}
编译运行:
问:子父类的函数可以发生重载不?
class BaseCls{public: int Add(int x, int y) { return x + y; }};class SubCls : public BaseCls{public: int Add(int x, int y, int z) { return x + y + z; }};int main(void){ SubCls s1; s1.Add(1, 2); return 0;}
若子类的Add(int x, int y, int z)能和父类的Add(int x, int y)发生重载,那么”s1.Add(1, 2);”将能正确调用。
编译运行:
提示说找不到”int SubCls::Add(int, int, int)”这个函数,可见,子类的”int Add(int x, int y, int z)”隐藏了父类的”int Add(int x, int y)”,这种情况称之为函数覆盖。也就是说子父类的不可发生函数重载。重载是发生在相同的作用域的,子父类的函数不在同一作用域自然不可发生重载。
要调用”Add(int x, int y)”函数,还是要加上作用域分辨符:
s1.BaseCls::Add(1, 2);
问:子类可以定义父类原型相同的函数吗?
class BaseCls{public: int print() { std::cout << "BaseCls::print()" << std::endl; }};class SubCls : public BaseCls{public: int print() { std::cout << "SubCls::print()" << std::endl; }};int main(void){ SubCls s1; s1.print(); return 0;}
编译运行:
可见,子类可以定义父类原型相同的函数。要访问父类的原型相同的函数是还是要使用作用域分辨符。子类定义与父类原型相同的函数,称之为函数重写。函数重写是函数覆盖的一种特殊情况。
2. 赋值兼容
问:父类对象可以为子类对象赋值吗?反过来呢?
class BaseCls{public: int print() { std::cout << "BaseCls::print()" << std::endl; }};class SubCls : public BaseCls{public: int print() { std::cout << "SubCls::print()" << std::endl; } int a;};int main(void){ SubCls sub; BaseCls base; sub = base; //父类对象为子类对象赋值 return 0;}
编译报错:
反过来,子类对象可以为父类对象赋值:
int main(void){ SubCls sub; BaseCls base; base = sub; //子类对象为父类对象赋值 return 0;}
编译通过:
子类继承于父类,拥有父类的所有成员方法和成员变量,所以子类对象可以当做父类对象使用,这便是子父类兼容性原则。它体现在:
(1) 子类对象可以直接初始化、赋值给父类对象
(2) 父类指针可以直接指向子类对象,父类引用可以直接引用子类对象
当使用父类指针或引用指向子类对象时,
(1) 子类对象退化为父类对象
(2) 只能访问父类中定义的成员
(3) 通过指针或者引用可以直接访问被子类覆盖的同名成员
int main(void){ SubCls sub; BaseCls *p_base = NULL; p_base = ⊂ p_base->a = 100; //错误 return 0;}
编译出错:
3. 赋值兼容遇上函数重写
如上代码,子类父类中都定义了函数原型相同的”print()”,根据赋值兼容性原则,父类指针可以指向基类对象,当然也可以指向父类对象:
void printType(BaseCls* p){ p->print();}int main(void){ SubCls sub; BaseCls base; printType(&sub); printType(&base); return 0;}
全局函数printType(BaseCls* p)的p指针参数先后指向子类对象和父类对象,并调用各自的print()函数,从程序设计的思路来看,希望第1次”p->print();”打印”SubCls::print()”,第2次”p_base->print();”打印”BaseCls::print()”。但是根据赋值兼容性原则,父类指针指向子类对象时,子类对象退化为父类对象,只能访问父类中定义的成员。所以不可能通过p_base指针访问到子类的print()函数:
两个”p_base->print();”打印的都是父类的print()函数,为什么?
在编译期间,编译器只能根据指针的类型判断所指对象的类型,而p指针究竟是指向父类对象还是子类对象,编译器在编译期间并不知道,但是编译器没理由报错,于是编译器采取了认为最安全的做法,即调用父类的print()函数,因为父类和子类肯定都具有print()函数(可是偏偏子类的print()函数已经重写了)。
程序员想要的效果是对printType()的两次调用,得到的分别是子类和父类的print()函数,怎么搞?这就需要c++的多态。
4. 多态
多态指的是:程序根据实际的对象类型决定调用目标函数,产生的效果为同样的调用语句在实际运行时有多种不同的表现形态。在程序在,具体体现为根据实际的对象类型,调用对应对象的重写函数。
以上面printType()函数代码为例:
p->print();
(1) 当p指向的是父类对象时,调用
int print(){ std::cout << "BaseCls::print()" << std::endl;}
(2) 当p指向子类对象时,调用
int print(){ std::cout << "SubCls::print()" << std::endl;}
c++作为一门面向对象的高级语言,自然支持面向对象的三大特性之一–多态,它是通过虚函数来实现的:
(1) 用virtual声明的函数称为虚函数
(2) 虚函数被重写之后具有多态的特性。
也就是说,要实现c++的子父类的多态,其一是要在父类声明虚函数,其二是子类要重写父类的虚函数。
基于前面的代码,在父类BaseCls的print()函数加上virtual声明(子类的print()函数的virtual可加可不加):
class BaseCls{public: virtual int print() //声明这是一个虚函数 { std::cout << "BaseCls::print()" << std::endl; }};
编译运行:
综上,函数重写必须要用多态实现,否则没有重写的意义。
- C的多态
- C的多态
- 【C/C++】多态的概念
- [C++]多态的使用
- Objective-c的多态
- OO in C(2): C语言的多态实现
- OO in C(2): C语言的多态实现
- OO in C(2): C语言的多态实现
- 多态的内幕--(C++, C)语言两个版本
- C++_对多态的理解
- C语言的多态实现
- C语言的多态实现
- C语言的多态实现
- C语言的多态实现
- C 语言里面的多态
- C语言实现的多态
- 使用继承的多态(C#)
- C++,多态的实现,语法练习
- 四、高并发秒杀API之Web层设计与实现
- ios-发生请求的两种方式和缓存策略
- 手把手学习Mybatis
- windows安装TensorFlow gpu版本时候的bug;No module named "_pywrap_tensorflow" ;DLL load failed.
- Spring入门
- c++的多态
- sringMVC 转发到jsp时出现405错误:JSPs only permit GET POST or HEAD
- Unity导出IOS symlink Unity libraries的作用
- eclipse中的.project 和 .classpath文件的具体作用
- 如何注册Google账户
- hdu 6119(尺取)
- linux安装jdk以及eclipse
- 中医美容一例
- 关于箭头函数的理解摘记