1

次のように宣言されたクラスがあります。

class TestFoo {
public:
    TestFoo();
    virtual void virtualFunction();
    void nonVirtualFunction();
};

私はこのように実装しようとしています

TestFoo::TestFoo(){}
void TestFoo::nonVirtualFunction(){}

コンパイル時にエラーが返されます:

undefined reference to vtable for TestFoo

私は試した :

TestFoo::TestFoo(){}
void TestFoo::nonVirtualFunction(){}
void TestFoo::virtualFunction(){}

これらの投稿への回答と一致するOKをコンパイルします:

vtable への未定義の参照

vtable への未定義の参照

私を混乱させているのは、仮想関数を宣言することの要点は、それを定義する必要がないということだと思っていたことです。この例では、TestFoo のインスタンスを作成するつもりはありませんが、TestFoo から継承する (具体的な) クラスのインスタンスを作成する予定です。それでも、TestFoo のすべてのサブクラスに対して関数 nonVirtualFunction を定義したいと考えています。

うまくいきませんでしたか?

ありがとう !

4

4 に答える 4

7

仮想関数を宣言することの要点は、それを定義する必要がないということです

そうではありませんが、「この関数の実装を派生クラスの別のものに置き換えたいと思うかもしれません」と書かれています。

私はあなたの質問を誤解しているかもしれませんが、C++ で純粋な仮想メンバー関数を定義できるとは思わないことを暗示しているようです。次のように宣言できます。

virtual void virtualFunction() = 0;

通常、純粋仮想関数は定義されませんが、もちろん可能です。つまり、「この関数のデフォルトの実装は常に意味があるとは限らないため、ありませんが、オプトインできる実装を提供します」ということです。

ところで、クラスに仮想関数がある場合は、仮想デストラクタも定義する必要があります。これは、派生クラスへの基本クラス (スマート) ポインタを持つことは完全に合法である (そしてしばしば推奨される) ためです。仮想デストラクタがなくても、 object がdelete正しく dされない可能性があります。

于 2013-03-15T08:16:48.173 に答える
3

...仮想関数を宣言することの要点は、それを定義する必要がないということだと思いました...

その機能には、純粋仮想メソッドと呼ばれる機能があります。

virtual void virtualFunction() = 0;  // no linking error now

virtualメソッドを未実装のままにすることはできないことに注意してください。その理由は、ボディ内で宣言さvirtualれたすべてのメソッドに対してエントリが必要だからです。本体が見つからない場合、リンク エラーが発生します。classvtable

この制限の目的:
クラスが抽象的でない限り (つまり、少なくとも 1 つの仮想関数を持っている場合)、 のオブジェクトを宣言しないことをコンパイラーに保証する方法はありませんTestFoo。次の操作を行うとどうなりますか。

DerivedOfTestFoo obj1;
TestFoo obj2 = obj1, *p = &obj2; // object slicing
p->virtualFunction(); // where is the body?

その他の状況; コンストラクターにはvirtualメカニズムがありません:

TestFoo::TestFoo () {
  this->virtualFunction(); // where is the body?
}

コンパイラは、「後悔するよりも安全である方がよい」というルールに従っていると結論付けることができます。:)

于 2013-03-15T08:21:31.077 に答える
1

あなたの説明は、抽象クラスのケースと完全に一致します。仮想関数を次のように宣言します。

    virtual void VirtualFunction () = 0;

これは、このクラスで関数を実装していないことを意味します。その結果、クラスは抽象化されます。つまり、このクラスの生のオブジェクトはインスタンス化できません。

また、仮想デストラクタを提供する必要があります。

更新:いくつかの説明...

この言語を使用すると、非仮想関数を再定義できます。ただし、場合によっては間違ったバージョンが呼び出される可能性があります。

derived D;    // rB is a reference to base class but it
base & rB=D;  // points to an object of the derived class

rB.NonVirtualFunction ();  // The base-class version is called

このため、現在、非仮想関数を再定義することは強く推奨されていません。Scott Meyers の「効果的な C++、第 3 版: プログラムと設計を改善するための 55 の具体的な方法」、項目 36:「継承された非仮想関数を再定義しないでください」を参照してください。

項目 7: 「ポリモーフィックな基底クラスで仮想デストラクタを宣言する」も参照してください。例:

base * pB = new derived;
delete pB;                // If base's destructor is not virtual,
                          // ~derived() will not be called.

なぜすべてがデフォルトで仮想ではないのか疑問に思っている場合、その理由は、仮想関数の呼び出しが非仮想関数の呼び出しよりもわずかに遅いためです。ああ、仮想関数を持つクラスのオブジェクトは、それぞれさらに数バイトを占有します。

于 2013-03-15T08:25:24.030 に答える
0

この仮想関数を純粋な仮想関数として定義したくない場合は、virtual void virtualFunction() = 0;

于 2013-03-15T08:21:55.227 に答える