为什么派生类的实例从基类调用方法?

时间:2019-12-17 20:02:07

标签: c++

请查看示例1和示例2。
示例1与示例2的不同之处仅在于**类IA **的重写方法run() 问题写在最后。

示例1:

#include <iostream>

class IA {
    public:
    void run() {
        print();
    }

    void print() {
        std::cout << "IA::print() \n";
    }
};

class A : public IA {
    public:

    void print() {
        std::cout << "A:: \n";
    }
};

int main() {
    A a1;
    a1.run();

    A * ptr = new A;
    ptr->run();
}

此代码显示:

IA :: print()
IA :: print()

示例2:

#include <iostream>

class IA {
    public:
    void run() {
        print();
    }

    void print() {
        std::cout << "IA::print() \n";
    }
};

class A : public IA {
    public:
    void run() {
        print();
    }

    void print() {
        std::cout << "A:: \n";
    }
};

int main() {
    A a1;
    a1.run();

    A * ptr = new A;
    ptr->run();
}

此代码显示:

A ::
答::

为什么这样打印?有什么区别?

谢谢。

2 个答案:

答案 0 :(得分:3)

您选择不对print进行虚拟化。这意味着不会发生动态调度。在调用该方法的任何时候,都会使用本地类型信息来确定要调用的方法。

void run() {
    print();
}

因此,当您调用非虚拟print时,将调用从写入print的地方看到的任何本地run函数。

IA::run中,唯一可见的printIA::print。因此IA::run呼叫IA::print

A::run中,您可以同时看到IA::printA::print;但是第二个隐藏了第一个。因此称为A::print

对象的实际动态类型是什么都没有关系,因为您没有要求进行虚拟调度。您要求使用本地静态类型信息选择该函数。


您可以要求虚拟派遣:

class IA {
public:
  void run() {
    print();
  }

  virtual void print() {
    std::cout << "IA::print() \n";
  }
};

我将关键字virtual添加到了print。现在,第一个版本将称为A::的{​​{1}}版本。

某些语言使所有方法隐式虚拟。 C ++不会,因为虚拟方法会产生一些运行时开销,并且C ++会尝试不让您为未使用的东西付费。

答案 1 :(得分:0)

在第一个程序中,成员函数run是基类的函数。在函数中,指针this的静态类型为IA。根据{{​​1}}的静态类型在IA类中搜索功能打印。

在第二个程序中,类A具有运行和打印这两个功能。它们隐藏了IA类的相应功能。因此它们被称为

从C ++标准(13.2成员名称查找)

  

1成员名称查找确定名称的含义(id表达式)   在类范围内(6.3.7)。名称查询可能会导致模棱两可   在这种情况下,程序格式不正确。 对于id表达式,名称   查找从此类的范围开始; 为合格ID,名称   查找从嵌套名称说明符的范围开始。名称查询   发生在访问控制之前(6.4,第14条)。