【问题标题】:Inheritance, pseudo polymorphism继承、伪多态
【发布时间】:2023-03-20 20:28:02
【问题描述】:

在挖掘 STL 源代码(DinkumWare、SGI、STLport 等)并试图理解它们的实现选择(进展顺利)时,我发现了一些我觉得有点奇怪或者我从未运行过的东西进入之前。

通常,当希望在派生类中重载成员函数时,您会在基类成员函数签名前加上 virtual 关键字,但在 STL 源代码中的不同点,情况并非如此。

这是我在 STL 实现中看到的精简版:

template <typename T> class A {
public:
    void func( ) { std::cout << "inside A func( )" << std::endl; }
};

template <typename T> class B : public A<T> {
public: 
    void func( ) { std::cout << "inside B func( )" << std::endl; }
};

编译器似乎对这种伪多态性很好,正如我所期望的那样,会出现以下错误:

error C2535: 'void B<T>::func(void)': member function already defined or declared

有人能解释一下这里发生了什么吗?

PS:这似乎也可以在没有模板的类的情况下工作。

'问候

【问题讨论】:

    标签: c++ inheritance stl polymorphism metaprogramming


    【解决方案1】:

    B&lt;T&gt;::func 成员只需 shadows A&lt;T&gt;::func。当你调用p-&gt;func(),其中A&lt;T&gt; *p 指向B&lt;T&gt;A&lt;T&gt;::func 被调用,所以没有多态性。

    #include <iostream>
    
    struct A
    {
        void func() { std::cout << "Hello!\n"; }
    };
    
    struct B : public A
    {
        void func() { std::cout << "Goodbye!\n"; }
    };
    
    int main()
    {
        B b;
        A *p = &b;
    
        p->func();
        b.func();
    }
    

    (Demo)

    在 C++ 标准中,至少有一个地方利用了这种阴影/名称隐藏:std::ifstream::rdbufhides its ancestor's method by that name,实际上改变了它的返回类型。

    【讨论】:

    • 谢谢,你每天都能学到新东西!既然你提到它确实有道理。
    【解决方案2】:

    这是可接受的代码,B&lt;T&gt;::func 只是隐藏了A&lt;T&gt;::func

    A<int> a;
    B<int> b;
    a.func(); // inside A
    b.func(); // inside B
    
    A<int> *const pA = new B<int>();
    pA->func(); // inside A
    

    当通过多态类型调用func时,会根据指针的类型调用函数。

    【讨论】:

      【解决方案3】:

      显然没有错误,因为这些函数只是重载:A::func() 的签名采用 A 对象(引用或指针)作为第一个参数,而 B::func() 的签名采用 B 对象作为第一个论点。也就是说,这只是重载了两个参数不同但函数名不同的函数。

      这是在几个地方完成的,以从一个函数产生不同的返回类型,该函数本质上是简单地转发到另一个函数(至少,这些是我能想到的地方)。这只是为了让用户的生活更轻松,尽管它实际上比其他任何事情都更令人困惑。我能想到的示例(例如流中的 rdbuf() 函数)应该创建一个不同的名称。

      【讨论】:

        【解决方案4】:

        没有virtual 关键字 - 重新定义函数时,您隐藏了超级函数。

        在你的例子中,通过重新定义func(),你告诉编译器B 有一个新函数,它与A 不同。

        尽管如此,因为它没有被声明为virtual,所以只有在从typeB 的变量中调用func() 时才会看到这种影响。拥有BA 类型的变量将调用A 的func()。

        A *a = new B;
        a->func()
        

        将调用第一个 [A's] 方法。

        要调用B的方法,需要类型为B

        B *b = new B;
        b->func()
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-12-19
          • 2020-07-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-08-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多