1 父子间的赋值兼容
-
子类对象可以当作父类对象使用(兼容性)
- 子类对象可以直接赋值给父类对象
- 子类对象可以直接初始化父类对象
- 父类指针可以直接指向子类对象:多态
- 父类引用可以直接引用子类对象
-
示例:子类对象的兼容性
-
Demo
#include <iostream> #include <string> using namespace std; class Parent { public: int mi; void add(int i) { mi += i; } void add(int a, int b) { mi += (a + b); } }; class Child : public Parent { public: int mv; void add(int x, int y, int z) { mv += (x + y + z); } }; int main() { Parent p; Child c; p = c; Parent p1(c); Parent& rp = c; //父类引用:引用子类对象 Parent* pp = &c; //父类指针:指向子类对象 rp.mi = 100; rp.add(5); // 没有发生同名覆盖 rp.add(10, 10); // 没有发生同名覆盖 //编译错误 pp->mv = 1000; pp->add(1, 10, 100); return 0; }
-
编译
test.cpp: In fuction 'int main()': test.cpp:51: error: 'class Parent' has no member named 'mv' test.cpp:52: error: no matching function for call to 'Parent::add(int,int,int)' test.cpp:11:note: candidates are: void Parent::add(int) test.cpp:16:note: void Parent::add(int,int)
-
-
当使用父类指针(引用)指向子类对象时
- 子类对象退化为父类对象
- 只能访问父类中定义的成员
- 可以直接访问被子类覆盖的同名成员,如父类的
add(int)
和add(int,int)
被子类的add(int,int,int)
所覆盖
2 特殊的同名函数
-
子类中可以重定义父类中已经存在的成员函数:重定义的原因是父类中的函数不能满足使用
-
这种定义发生在继承中,叫做函数重写
-
函数重写是同名覆盖的一种特殊情况
class Parent { public: void print() { cout << "I'm Parent." << endl; } }; //函数重写 class Child : public Parent { public: //函数原型与父类中的完全相同,此处函数重写是必须的 void print() { cout << "I'm Child." << endl; } };
-
问题:当函数重写遇到赋值兼容会发生什么?
-
示例:函数重写 VS 赋值兼容
-
Demo
#include <iostream> #include <string> using namespace std; class Parent { public: int mi; void add(int i) { mi += i; } void add(int a, int b) { mi += (a + b); } //打印父类信息 void print() { cout << "I'm Parent." << endl; } }; class Child : public Parent { public: int mv; void add(int x, int y, int z) { mv += (x + y + z); } //重写函数:打印子类信息 void print() { cout << "I'm Child." << endl; } }; void how_to_print(Parent* p) { p->print(); } int main() { Parent p; Child c; p.print(); c.print(); how_to_print(&p); // Expected to print: I'm Parent. how_to_print(&c); // Expected to print: I'm Child. return 0; }
-
编译运行
I'm Parent. I'm Child. I'm Parent. I'm Parent. !!!!!!
-
-
问题分析
-
编译期间,编译器只能根据指针的类型判断所指向的对象
-
根据赋值兼容,编译器认为父类指针指向的是父类对象
-
因此,编译结果只可能是调用父类中定义的同名函数
-
在编译下面的函数时,编译器不可能知道指针
p
究竟指向了什么,但是编译器没有理由报错。于是编译器认为最安全的做法是调用父类的print
函数,因为父类和子类肯定都有相同的print
函数。但不是期望的! => 使用多态解决void how_to_print(Parent* p) { p->print(); }
-