棘手的多态性和虚函数

时间:2014-06-26 19:20:16

标签: 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; 
} 

该计划的输出将是:

add_st (K*) from K

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

但是这段代码:

#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; 
} 

将打印

add_st (L*) from L

为什么?

2 个答案:

答案 0 :(得分:6)

参数列表中的虚函数不变,返回类型上的协变

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

例如,给定

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

合同是 add_st接受K 类型的任何对象(通过指针),返回K类型的对象(通过指针)。

这会覆盖它

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

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

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

因为返回是M类型的对象,通过继承也是K类型的对象;合同得到满足。

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

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

因为它无法接受 K类型的任何对象,只能接受类型K和类型N的对象。这两个都不是:

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

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

他们将创建一个新函数(v表中的新插槽),它重载并隐藏现有函数,而不是覆盖它。 (正如约翰史密斯在他的回答中所说,可以使用 using-declaration 来避免隐藏基础版本)

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

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

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

有关 variance 的详细信息,您可能需要了解Liskov Substitution Principle

答案 1 :(得分:1)

首先,函数签名包括函数名称及其参数类型。在第一个示例中,函数名称相同,但其参数类型不同。因此他们有不同的签名。因此,在您的第一个示例中,子类中的函数未覆盖其父级中的函数。

其次,还有overload和名称隐藏的概念。在您的情况下,第一个示例中的函数定义隐藏其父函数。如果将父函数放入同一范围,子函数将重载父函数,如此

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