继承(二)
程序员文章站
2022-07-17 08:58:07
[TOC] 1. 父子间的同名冲突 同名变量冲突 子类可以定义父类中的同名成员变量 父类中的同名成员变量被隐藏,但仍然存在于子类中 父类中的同名成员变量需要通过作用域分辨符(::)访问 cpp class Parent { public: void print() { cout include us ......
目录
1. 父子间的同名冲突
同名变量冲突
- 子类可以定义父类中的同名成员变量
- 父类中的同名成员变量被隐藏,但仍然存在于子类中
- 父类中的同名成员变量需要通过作用域分辨符(::)访问
child c; c.mi = 100; //访问子类中的mi c.parent::mi = 1000; //访问父类中的mi
#include <iostream> #include <string> using namespace std; namespace a { int g_i = 0; } namespace b { int g_i = 1; } class parent { public: int mi; parent() { cout << "parent() : " << "&mi = " << &mi << endl; } }; class child : public parent { public: int mi; child() { cout << "child() : " << "&mi = " << &mi << endl; } }; int main() { child c; c.mi = 100; c.parent::mi = 1000; cout << endl; cout << "&c.mi = " << &c.mi << endl; cout << "c.mi = " << c.mi << endl; cout << endl; cout << "&c.parent::mi = " << &c.parent::mi << endl; cout << "c.parent::mi = " << c.parent::mi << endl; return 0; }
同名函数冲突
- 子类中的成员函数将隐藏父类的同名成员函数(只需要同名即可,对参数列表和返回值类型无要求)
- 使用作用域分辨符访问父类中的同名成员函数
- 子类可以定义与父类完全相同的成员函数(包括函数名,参数列表、返回值类型)
- 子类无法重载父类中的成员函数
#include <iostream> #include <string> using namespace std; class parent { public: int mi; void add(int v) { mi += v; } void add(int a, int b) { mi += (a + b); } }; class child : public parent { public: int mi; void add(int v) { mi += v; } void add(int a, int b) { mi += (a + b); } void add(int x, int y, int z) { mi += (x + y + z); } }; int main() { child c; c.mi = 100; c.parent::mi = 1000; cout << "c.mi = " << c.mi << endl; cout << "c.parent::mi = " << c.parent::mi << endl; c.add(1); c.add(2, 3); c.add(4, 5, 6); c.parent::add(1); c.parent::add(2, 3); cout << "c.mi = " << c.mi << endl; cout << "c.parent::mi = " << c.parent::mi << endl; return 0; }
2. 同名冲突引发的问题
父子间的赋值兼容
父子间的赋值兼容,指的是子类对象可以当作父类对象使用,具体表现在两个方面
- 子类对象可以直接初始化父类对象,或者给父类对象赋值
- 父类指针(引用)可以直接指向(引用)子类对象
- 实际上仅仅指向(引用)了子类对象中的父类部分
- 通过父类指针(引用),只能访问父类中的成员变量和成员函数
- 子类对象本身不受影响,依然可以访问自身的成员
#include <iostream> #include <string> using namespace std; class parent { public: int mi; parent(int i = 0) { mi = i; } void add(int i) { mi += i; } void add(int a, int b) { mi += (a + b); } }; class child : public parent { public: int mv; child(int v = 0) { mv = v; } 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); cout << "p.mi = " << p.mi << endl; cout << "c.mv = " << c.mv << endl; cout << "rp.mi = " << rp.mi << endl; cout << "pp->mi = " << pp->mi << endl; cout << endl; /*编译不过,只能访问父类中的成员*/ // pp->mv = 1000; // pp->add(1, 10, 100); /*父类指针或引用指向子类对象,对子类对象本身没有影响*/ c.mv = 1000; c.add(1, 1, 1); cout << "p.mi = " << p.mi << endl; cout << "c.mv = " << c.mv << endl; cout << "rp.mi = " << rp.mi << endl; cout << "pp->mi = " << pp->mi << endl; return 0; }
函数重写
- 子类可以重定义父类中已经存在的成员函数
- 这种重定义发生在继承中,叫做函数重写
- 函数重写是同名覆盖的一种特殊情况
class parent { public: void print() { cout << "i'm parent." << endl; } }; class child : public parent { public: void print() { cout << "i'm child." << endl; } };
当函数重写遇上赋值兼容
当函数重写遇上赋值兼容时会发生什么,下面的示例代码展示了这个问题。
#include <iostream> #include <string> using namespace std; class parent { public: void print() { cout << "i'm parent" << endl; } }; class child : public parent { public: void print() { cout << "i'm child" << endl; } }; void how_to_print(parent *p) { p->print(); } int main() { parent p; child c; how_to_print(&p); // expected: i'm parent how_to_print(&c); // expected: i'm child return 0; }
第34-35行注释部分展示了我们期望的结果,但实际运行结果和期望不符,原因在于
- 编译期间,编译器只能根据指针(引用)的类型判断所指向(引用)的对象
- 根据赋值兼容,编译器认为父类指针(引用)指向(引用)的是父类对象
- 因此,编译结果只可能是调用父类中定义的同名函数
编译器的处理方法是合理的,但不是我们期望的,这就是同名冲突引发的问题,要想解决这个问题,需要用到多态的知识。
3. 继承中的强制类型转换
我们之前讲过了c++的四种强制类型转换,那么在继承中如何正确地使用强制类型转换呢?
- dynamic_cast是与继承相关的类型转换关键字
- dynamic_cast要求相关的类中必须有虚函数
- dynamic_cast用于有直接或者间接继承关系的类指针(引用)之间
-
指针
:转换成功,得到目标类型的指针;转换失败,得到一个空指针 -
引用
:转换成功,得到目标类型的引用;转换失败,得到一个异常操作信息 - 编译器会检查dynamic_cast的使用是否正确
- 类型转换的结果只可能在运行阶段才能得到
/* ** 继承中的dynamic_cast关键字 */ #include <iostream> #include <string> using namespace std; class base { public: base() { cout << "base::base()" << endl; } /* * dynamic_cast要求类中必须有虚函数,因此将父类的析构函数定义为虚函数 */ virtual ~base() { cout << "base::~base()" << endl; } }; class derived : public base { }; int main() { base *p0 = new derived; base *p1 = new base; base &p2 = *p1; derived *pd0 = dynamic_cast<derived *>(p0); //dynamic_cast转换指针成功,得到目标类型指针 derived *pd1 = dynamic_cast<derived *>(p1); //不能用子类指针指向一个父类对象,dynamic_cast转换指针失败,得到的pd1为null cout << "pd0 = " << pd0 << endl; cout << "pd1 = " << pd1 << endl; derived &pd2 = dynamic_cast<derived &>(p2); //dynamic_cast转换引用失败,运行时抛出异常 delete p0; delete p1; return 0; }
上一篇: 波尔多红酒节,红酒的这些功效你可能不知道
下一篇: 如何挑选红曲米?小编三招教你搞定