【问题标题】:C++ - want virtual acting solely as redirectionC ++ - 希望虚拟仅作为重定向
【发布时间】:2010-11-04 00:53:51
【问题描述】:

假设我有一个模板:

template <class N, class I>
void add(N* element, std::list<N*> & container, I (N::*f)() const,
        std::string successmsg, std::string exceptmsg) {
//...
}

我想调用它以获得指向派生类的基类指针列表。

add(newAirplane, airplanes, &Airplane::getRegistration,
        "Added!", "Error: Existent!");

Airplane 继承自 AirplaneType

当然不会编译,N先定义为AirplaneType再定义为Airplane

我添加了一个虚拟的getRegistration@AirplaneType 但是编译器当然会给出一个 vtable 错误。

解决这个问题的正确方法是什么? AirplaneType 没有 registration 属性,我对它不感兴趣。我也想避免virtual getRegistration() const {return "";}

有什么好的做法建议吗?

编辑:

感谢您的回答,但仍然无法正常工作。我想我找到了剩下的问题,但没有找到解决方案:

void Airline::addAirplane(AirplaneType* airplane) {
add(newAirplane, airplanes, &Airplane::getRegistration,
        "Added!", "Error: Existent!");

}

收到的指针类型是AirplaneType,而不是Airplane

airplanesAirplaneType 指针的列表。

【问题讨论】:

  • 如果指针只有AirplaneType*,那么你在做什么尝试调用Airplane的函数呢?如果引用必须是Airplane 的实例,则可以static_cast 指针。如果不是,那么如果AirplaneType 有任何虚函数,你可以dynamic_cast,但是如果引用不是Airplane 的实例,你打算怎么做?
  • @Francisco:剩下的问题与模板无关。如果f 不是element 的成员函数,则不能在element 上调用f。不要介意模板,C++ 不够灵活,无法做到这一点。如果你的函数AddAirplane 被传递一个指向Helicopter 的指针,其中Helicopter 派生自AirplaneType,而不是Airplane,该怎么办?当add 需要在Helicopter 上调用Airplane::getRegistration 时应该发生什么。如果这是不可能的,实际上AirplaneType* 必须指向Airplane,那么理想情况下addAirplane 应该采用Airplane*
  • @Francisco:当然,如果您将getRegistration 设为AirplaneType 的虚函数,那么您就可以了,因为您可以在任何AirplaneType* 上调用它。那么你就不再需要模板中的额外类型了。
  • @Francisco:是的,可以做到。指向虚拟成员函数的指针按照您希望的方式工作 - 即使对象指针和成员函数指针都是基类类型,也会调用正确的覆盖。我已经编辑了我的答案来演示。

标签: c++ class inheritance virtual


【解决方案1】:

您需要另一个模板参数,因为您关心两个不同的类 - 指针的类型(以及您要使用它调用的成员函数)和容器的类型:

#include <list>

struct AirplaneType {
};

struct Airplane : AirplaneType {
    int check() const { return 3; }
};

template <typename T, typename U, typename I>
void add(T* element, std::list<U*> & container, I (T::*f)() const) {
    container.push_back(element);
    I i = (element->*f)();
}

int main() {
    std::list<AirplaneType*> ls;
    Airplane a;
    add(&a, ls, &Airplane::check);
}

在这种情况下,我的add 函数并没有真正使用containerlist 的事实,因此更合理的版本可能是:

template <typename T, typename U, typename I>
void add(T* element, U & container, I (T::*f)() const) {
    container.push_back(element);
    I i = (element->*f)();
}

然后,你可以进一步抽象:

template <typename T, typename U, typename AUF>
void add(T element, U & container, AUF func) {
    container.push_back(element);
    typename AUF::result_type i = func(element);
}

...但这对调用者来说不太方便:

#include <functional>

add(&a, ls, std::mem_fun(&Airplane::check));

有什么好的做法建议吗?

不要创建包含原始指针的容器。

编辑:使用我的每个选项来使用虚拟功能:

#include <list>
#include <functional>
#include <iostream>

struct AirplaneType {
    virtual int check() const { return 0; }
};

struct Airplane : AirplaneType {
    int check() const { std::cout << "check\n"; return 3; }
};

template <typename T, typename U, typename I>
void add(U* element, std::list<T*> & container, I (U::*f)() const) {
    container.push_back(element);
    I i = (element->*f)();
}

template <typename T, typename U, typename AUF>
void add2(T element, U & container, AUF func) {
    container.push_back(element);
    typename AUF::result_type i = func(element);
}

int main() {
    std::list<AirplaneType*> ls;
    Airplane a;
    add(static_cast<AirplaneType*>(&a), ls, &AirplaneType::check);
    add2(&a, ls, std::mem_fun(&AirplaneType::check));
}

输出是:

check
check

这表明即使函数指针指向AirplaneType::check,而不是Airplane::check,也正确调用了覆盖。

【讨论】:

    【解决方案2】:

    您需要为公共基础添加一个额外的模板参数,因为 C++ 不处理逆变类型。也就是说,std::list&lt;Airplane*&gt; 是与std::list&lt;AirplaneType*&gt; 完全不同的类型,并且不会发生从指向最多派生到最少派生的指针列表的隐式转换。因此,实际上您的 add 函数需要变为:

    template <class N, class I, class B>
    void add(N* element, std::list<B*> & container, I (N::*f)() const,
            std::string successmsg, std::string exceptmsg)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-01
      • 1970-01-01
      • 2019-04-16
      • 2013-07-30
      • 2013-02-05
      • 2011-08-23
      相关资源
      最近更新 更多