最近遇到一个很有意思的问题,如果在父类成员函数中调用子类成员函数。这个应用场景不是很常见,但是有一些方法可以实现。
通过函数指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <iostream>
class Derived;
class Base { public: Derived *d; void (Derived::*f)(int); void f1() { (d->*f)(123); } };
class Derived : public Base { public: void f2(int i) { std::cout << "Derived f2: " << i << std::endl; } void f3() { d = this; f = &Derived::f2; } };
int main() { Derived d; d.f3(); d.f1(); return 0; }
|
输出:
思路是这样的,在父类中声明子类成员变量指针和函数指针,并在父类成员方法中通过函数指针去调用。然后在子类中给继承得到的这些成员变量赋值,这样子类调用父类的方法的时候就能实现调用子类方法。
这里说两个关于类成员函数指针的知识点:
- 函数指针赋值要使用 &
- 使用 .* (实例对象)或者 ->*(实例对象指针)调用类成员函数指针所指向的函数
通过模板类
其实这个问题本来是让我用模板类实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <iostream>
template <typename T> class Base { public: T *t; void f1() { std::cout << "Base f1: "<< t->val << std::endl; t->f2(); } };
class Derived : public Base<Derived> { public: int val; void f2() { std::cout << "Derived f2: " << val << std::endl; }
void f3() { val = 123; t = this; } };
int main() { Derived d; d.f3(); d.f1(); return 0; }
|
输出:
1 2
| Base f1: 123 Derived f2: 123
|
其实思路类似,就是在父类中需要有一个占位符能表示子类。在前面函数指针实现中,我们用子类指针和函数指针作为声明(需要前置声明子类)占位符。而利用模板,我们不需要函数指针,模板类型本身就可以作为一种占位符。我们将模板类型当作子类类型,直接在父类成员中调用子类函数。另外,我们在子类定义时,继承的父类是利用子类模板实例化的一个类,然后在子类中给模板类型赋值为当前对象指针,这样就实现了调用。
其实这里我们主要是利用 t 是模板类型 T 的指针,这样即使不知道 t 的具体定义,也可以访问到其成员,可以调用到其方法,若非通过模板,就不能这样。