棘手的多态性和虚函数

Rom*_*dik 1 c++ polymorphism virtual-functions

我有以下代码.

#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; 
} 
Run Code Online (Sandbox Code Playgroud)

该计划的输出将是:

add_st (K*) from K
Run Code Online (Sandbox Code Playgroud)

如果我没有错过任何东西的原因是虚拟功能表.从层次结构的顶部到最低的类生成对象.

但是这段代码:

#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; 
} 
Run Code Online (Sandbox Code Playgroud)

会打印

add_st (L*) from L
Run Code Online (Sandbox Code Playgroud)

为什么?

Ben*_*igt 6

虚函数在参数列表上是不变的,并且在返回类型上是协变的.

想到这一点的基本方法是,在基类中引入虚拟成员函数的地方,它定义了一个契约.

例如,给定

struct K
{ 
    virtual K* add_st(K* n);
};
Run Code Online (Sandbox Code Playgroud)

契约是add_st接受任何类型的对象K(通过指针),并返回一个类型的对象K(通过指针).

这将覆盖它

struct L : K
{ 
    virtual K* add_st(K* a);
};
Run Code Online (Sandbox Code Playgroud)

因为合同明显得到满足,因此:

struct M : K
{ 
    virtual M* add_st(K* a);
};
Run Code Online (Sandbox Code Playgroud)

因为返回是一个类型的对象M,它通过继承也是一个类型的对象K; 合同满意.

但是这(问题中的情况)不会覆盖

struct N : K
{ 
    virtual K* add_st(N* a);
}; 
Run Code Online (Sandbox Code Playgroud)

因为它不能接受任何类型的对象K,只能接受类型K和类型的对象N.这也不是:

struct P : K
{ 
    virtual K* add_st(void* a);
};
Run Code Online (Sandbox Code Playgroud)

即使从类型理论的角度来看,逆变参数也是兼容的,事实是C++支持多重继承,而有时候需要指针调整,因此逆向参数类型在实现级别上会中断.

它们将创建一个新函数(v表中的新插槽),它重载并隐藏现有函数,而不是覆盖它.(正如John Smith在他的回答中所说,可以使用using声明来避免隐藏基本版本)

以下是错误,因为签名是相同的,但返回类型是不兼容的:

struct Q : K
{ 
    virtual void* add_st(K* a);
};
Run Code Online (Sandbox Code Playgroud)

这里的结果可以是任何对象类型,但这还不够好,合同需要一个类型的对象K.它不能覆盖现有的功能,因为参数没有区别.所以它被拒绝了.

有关方差的更多详细信息,您可能需要阅读Liskov替换原则.