2

この質問は、仮想関数のオーバーライドと非仮想関数の非表示の違いは何ですか?に少し関連しています。、しかし、私は技術的な詳細についてではなく、非仮想関数と仮想関数の使用について尋ねています。

ここで少し背景を説明します。基本クラス A と 2 つの派生クラス B および C があるとします。

#include <iostream>

class A {
public:
  A() {};
  virtual void foo() { std::cout << "foo() called in A\n"; };
  virtual void bar() { std::cout << "bar() called from A\n"; };
  void xorp() { std::cout << "xorp() called from A\n"; };
  virtual ~A() {};
};

class B : public A {
public:
  B() {};
  virtual ~B() {};
  virtual void foo() override { std::cout << "foo() called in B\n"; };
  //virtual void bar() override not implemented in B, using A::bar();
};

class C : public A {
public:
  C() {};
  virtual ~C() {};
  virtual void foo() override { std::cout << "foo() called in C\n"; };
  //virtual bar() override not implemented in C, using A::bar();
};

int main() {
  A a{};
  B b{};
  C c{};

  a.foo(); //calls A::foo()
  a.bar(); //calls A::bar()
  a.xorp(); //calls non-virtual A::xorp()

  b.foo(); //calls virtual overridden function B::foo()
  b.bar(); //calls virtual non-overridden function A::bar()
  b.xorp(); //calls non-virtual A::xorp()

  c.foo(); //calls virtual overridden function C::foo()
  c.bar(); //calls virtual non-overridden function A::bar()
  c.xorp(); //calls non-virtual A::xorp()

  return 0;
}

これにより、予想どおり、次のように出力されます。

foo() called in A
bar() called from A
xorp() called from A
foo() called in B
bar() called from A
xorp() called from A
foo() called in C
bar() called from A
xorp() called from A

派生クラスで仮想関数 bar() を実装しないままにしておくと、派生クラス B および C での bar() への呼び出しはすべて A::bar() に解決されます。非仮想関数である xorp() は、派生クラスから b.xorp() または bA::xorp() として呼び出すこともできます。

たとえば、B に xorp() を実装すると、A::xorp() が効果的に非表示になり、b.xorp() の呼び出しは実際には bB::xorp() の呼び出しになります。

上記の例を使用して、私の質問に行きます。派生クラスの実装に必要なヘルパー関数があるとします。

ヘルパー関数を非仮想メンバー関数 (xorp() など) にすることと、ヘルパー関数を派生クラスがオーバーライドしない仮想関数 (bar()) にすることの間に違いはありますか?

クラス オブジェクト レイアウトと VTABLE に関するこのプレゼンテーション ( https://www.cs.bgu.ac.il/~asharf/SPL/Inheritance.pptx、スライド 28 ~ 35) を読んでも、実際には違いを見つけることができませんでした。 -仮想関数とオーバーライドされていない仮想関数が同じ場所 (基本クラスの関数) を指している

これら2つのアプローチが異なる結果をもたらす例、または私が見つけていない警告がある場合、誰かが私に例を挙げてもらえますか?

4

2 に答える 2

3

あなたの例の欠点は、ポリモーフィズムを使用していないことです。すべてのオブジェクトを直接操作します。呼び出しを動的に解決する必要がないため、オーバーライドに関連するものは何もありません。また、呼び出しが動的に解決されない場合、仮想関数と非仮想関数の間に違いはまったくありません。違いを確認するには、ヘルパー フリー関数を使用します。

void baz(A& a) {
  a.foo();
  a.bar();
  a.xorp();
}

int main() {
  // As before
  baz(a);
  baz(b);
  baz(c);
}

fooこれで、 、 、barへの呼び出しbazが解決される方法に顕著な違いがあることがわかるはずです。特に...

たとえば、B に xorp() を実装すると、A::xorp() が効果的に非表示になり、b.xorp() の呼び出しは実際には bB::xorp() の呼び出しになります。

... 内では true ではなくなりますbaz

于 2018-11-11T12:36:38.243 に答える