简体   繁体   English

棘手的多态性和虚函数

[英]Tricky Polymorphism and virtual functions

I have the following code. 我有以下代码。

#include <iostream> 
using namespace std; 

class K { 
public: 
    virtual void add_st(K* n) {
      cout << "add_st (K*) from K\n";
    } 
};

class L: public K { 
public: 
    virtual void add_st(L* a) {
      cout << "add_st (L*) from L\n";
    } 
}; 

int main() { 
    L ob, ob2;
    K  k, *pl = &ob; 
    pl->add_st(&ob2); 
    return 0; 
} 

The output of this program will be: 该程序的输出为:

add_st (K*) from K

The reason why if I didn't miss anything is Virtual Function Table. 如果我什么都没错过的原因是虚函数表。 The object is generated from the top of hierarchy down to the lowest class. 从层次结构的顶部一直到最低的类生成对象。

But this code: 但是这段代码:

#include <iostream> 
using namespace std; 

class K { 
public: 
    virtual void add_st() {
      cout << "add_st (K*) from K\n";
    } 
};

class L: public K { 
public: 
    virtual void add_st() {
      cout << "add_st (L*) from L\n";
    } 
}; 

int main() { 
    L ob, ob2;
    K  k, *pl = &ob; 
    pl->add_st(); 
    return 0; 
} 

Will print 将打印

add_st (L*) from L

Why? 为什么?

Virtual functions are invariant on the argument list, and covariant on the return type. 虚函数在参数列表上是不变的,在返回类型上是协变的。

An elementary way to think of this is that where the virtual member function is introduced in the base class, it defines a contract. 想到这一点的基本方法是,在基类中引入了虚拟成员函数的地方,它定义了一个契约。

For example, given 例如,给定

struct K
{ 
    virtual K* add_st(K* n);
};

the contract is that add_st accepts any object of type K (by pointer), and returns an object of type K (by pointer). 契约是add_st接受任何类型为K对象 (通过指针),并返回类型为K的对象 (按指针)。

This will override it 这将覆盖它

struct L : K
{ 
    virtual K* add_st(K* a);
};

because the contract is clearly met, and so will: 因为已清楚地履行了合同,所以:

struct M : K
{ 
    virtual M* add_st(K* a);
};

because the return is an object of type M , which by inheritance also is an object of type K ; 因为return是M类型的对象,通过继承它也是K类型的对象; the contract is satisfied. 合同已履行。

But this (the case in the question) does not override 但这(问题中的情况)不会被覆盖

struct N : K
{ 
    virtual K* add_st(N* a);
}; 

because it cannot accept any object of type K , only ones which are both type K and type N . 因为它不能接受任何类型为K对象,所以只能接受类型均为KN And neither is this: 这也不是:

struct P : K
{ 
    virtual K* add_st(void* a);
};

even though from a type-theoretic perspective, contravariant parameters would be compatible, the truth is that C++ supports multiple inheritance and upcasts sometimes require pointer adjustment, so contravariant parameter types break at the implementation level. 即使从类型理论的角度来看,逆向参数是兼容的,事实是C ++支持多重继承,并且上载有时需要指针调整,因此逆向参数类型在实现级别中断。

They will create a new function (new slot in the v-table) that overloads and hides the existing function, instead of overriding it. 他们将创建一个新函数(v表中的新插槽),该函数将重载并隐藏现有函数,而不是覆盖现有函数。 (As John Smith says in his answer, a using-declaration can be used to avoid hiding the base version) (正如约翰·史密斯(John Smith)在回答中所说,可以使用using声明来避免隐藏基本版本)

And the following is an error, because the signature is the same, but the return type is incompatible: 以下是错误,因为签名相同,但是返回类型不兼容:

struct Q : K
{ 
    virtual void* add_st(K* a);
};

Here the result can be any object type, but that's not good enough, the contract requires an object of type K . 这里的结果可以是任何对象类型,但这还不够好,合约需要类型为K的对象。 And it can't override the existing function, because the parameters don't differ. 而且它不能覆盖现有函数,因为参数没有区别。 So it is just rejected. 因此它只是被拒绝。

For more details on variance , you may want to read about the Liskov Substitution Principle . 有关方差的更多详细信息,您可能需要阅读Liskov替代原理

First, function signature includes the function name and its parameter types. 首先,功能签名包括功能名称及其参数类型。 In your first example, the function name is the same but its parameter types are different. 在第一个示例中,函数名称相同,但其参数类型不同。 Therefore they have different signatures. 因此,它们具有不同的签名。 Therefore in your first example, the function in your child class did not override the function in its parent. 因此,在第一个示例中,子类中的函数未覆盖其父类中的函数。

Second there is also the concept of overload and name hiding. 其次还有overload和名称隐藏的概念。 In your case the function definition in the first example hides its parent function. 在您的情况下,第一个示例中的函数定义将隐藏其父函数。 If you bring the parent function in to the same scope, the child function will overload the parent function, like this 如果将父函数带入相同的作用域,则子函数将重载父函数,如下所示

class L: public K { 
public: 
    using K::add_st;
    virtual void add_st() {
        cout << "add_st (L*) from L\n";
}; 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM