3

最近、C ++の継承とポリモーフィズムに取り組んでみましたが、意味をなさない問題がいくつかあります。別々のファイルに2つのヘッダーがあり、実装を含むcppファイルがあります。私のコードの簡単な要約は次のとおりです。

#ifndef MANDEL_H_
#define MANDEL_H_

class Mandel{

public:
    virtual void compute("various arguments") = 0;

    //dummy destructor, I must have one or compile is sad and I dunno why
    virtual ~Mandel();
private:
    virtual int compute_point("various arguments") = 0;
};

#endif

これは「Mandel.h」と呼ばれる私の「祖父」ヘッダーです。次に、「父」ヘッダーに移動します。この次のヘッダーは、Mandelの白と黒の実装に固有で、「Black_White_Mandel.h」と呼ばれるいくつかの変数を指定します。

#ifndef BLACK_WHITE_MANDEL_H_
#define BLACK_WHITE_MANDEL_H_

#include "Mandel.h"

class Black_White_Mandel: public Mandel {

protected:
    int max_iterations; //a specific variable of this Black_White Version
};

#endif

そして、White_Black_Mandel_Imp1.cppという別のファイルで、Black_White_Mandelヘッダーの実装に従います。

#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#include "Mandel.h"
#include "Black_White_Mandel.h"

using namespace std;

//constructor
Black_White_Mandel::Black_White_Mandel(){
    max_iterations = 255;
}

//destructor
Black_White_Mandel::~Black_White_Mandel(){}

int Black_White_Mandel::compute_point("various arguments") {
    //code and stuff
    return 0;
}

void Black_White_Mandel::compute("various arguments") {
     //code and stuff
}

したがって、Mandel.hには2つの関数があり、それらは仮想で「= 0」であるため、実装する必要があります。White_Black_Mandel_Imp1.cppで、これらの関数を実装すると、コンパイラーはおかしくなります。関数はWhite_Black_Mandel.hで定義されておらず、それは真実ですが、Mandel.hで定義されていると書かれています。したがって、継承により、White_Black_Mandel_Imp1.cppは、Mandel.hからこれらの関数を実装する義務があることを知る必要があります。

わかりません。友人の1人が、私のWhite_Black_Mandel.hファイルはMandel.hの正確なコピーである必要があると言っていますが、いくつか追加されていますが、これは本当にばかげているように見えます。意味がありません。

私は何が間違っているのですか?

4

5 に答える 5

7

祖先クラスに2つの純粋仮想メソッドがありますが、これは、それらのプロトタイプを子クラスで使用する準備ができていることを意味するものではありません。

子クラスでもプロトタイプを宣言する必要があります。

class Black_White_Mandel: public Mandel {

public:
    virtual void compute("various arguments")

protected:
    int max_iterations; //a specific variable of this Black_White Version

private:
    virtual int compute_point("various arguments");
};

キーワードはオプションですが、virtualメソッドが実際に仮想であったことを知っておくと便利です。この特定のサブクラスにそれらを実装する必要はありません。何も指定しないようにすることはできますが、この子クラスのオブジェクトをインスタンス化できないように、実装する必要のある2つの純粋仮想メソッドがあります(とにかく階層ツリーの下位に実装します)。

他の点では同様の状況であるため、仮想デストラクタが必要です。

Base *derived = new Derived();
delete derived;

コンパイラは正しいデストラクタを呼び出すことができませんでした。

于 2013-03-15T00:26:10.947 に答える
4

computeとのプロトタイプを追加compute_pointBlack_White_Mandelます。

純粋仮想関数を持つ基本クラスから継承し、それらすべてを実装しない場合があります。派生クラスは抽象のままであり、すべての純粋仮想関数が実装されるまで、別のクラスなどから継承する必要があります。

例えば

class A {
    virtual void foo() = 0;
    virtual void bar() = 0;
};

class B : public A {
    virtual void foo() {};
};

class C : public B {
    virtual void bar() {};
};

class D : public A {
    virtual void foo() {};
    virtual void bar() {};
};

上記のインスタンス化可能なクラスはとのみCですD

于 2013-03-15T00:24:21.063 に答える
3

White_Black_Mandel_Imp1.cppは、それが義務を負っていることを知っている必要があります

そうではなく、すべきではありません。抽象クラスになることも決定できます。その場合、これらの関数をそのままにしておくことができます。

于 2013-03-15T00:25:58.763 に答える
3

実装クラスで宣言を提供する必要がある理由は、新しい戻り型が元の戻り型と共変である限り、派生クラスが異なる戻り型を使用して仮想関数をオーバーライドすることが合法であるためです。たとえば、基本クラスはを返すことができBaseReturnedObject&ますが、派生クラスにはを返すオプションがありますDerivedReturnObject&。派生クラスに宣言がないと、コンパイラはメソッドの戻り型が何であるかを認識しません。ベースと同じであるとは想定できないため、コンパイラにはプロトタイプが必要です。

共変リターン型でオーバーライドするルールについては、この質問を参照してください。

于 2013-03-15T00:29:28.713 に答える
0

「祖父」クラスに 4 つのメソッドがあり、そのすべてが純粋仮想であると想像してください。ここで、「父」クラスに 2 つ、「子」クラスに 2 つ実装する予定です。ところで、この特定の用語は理想的ではありませんが、あなたがそれから始めたので、私はそれに固執しています.

クラスを定義するとき (その間の .h ファイルでclass whatever {};基本クラス (用語では祖父クラス) のどの関数をオーバーライドするかをコンパイラーに伝えます。実装をヘッダーのすぐそこに配置するか、または.cpp ファイルに含めることができますが、オーバーライドする 4 つの関数 (存在する場合) を指定する必要があります。

クラス定義 (あなたが言っているヘッダー ファイル) で特定の継承された関数について言及していない場合、実装 (.cpp ファイル) をコンパイルするときに、コンパイラは「何? あなたは私に言わなかった.この関数をオーバーライドする予定です!」あなたの応答は、「おい、それは純粋な仮想です。オーバーライドしないと、オブジェクトをインスタンス化できません!」のようです。しかし、あなたは何を知っていますか?コンパイラはそれについてあまり気にすることができませんでした。インスタンス化可能なオブジェクトを作成しようとしているかどうか、さらに継承するつもりがあるかどうか、さらには関数が基本クラスで純粋な仮想であったかどうかさえわかりません。知っているのは、実装ファイルをコンパイルしていて、クラス定義で指定していない関数に遭遇したということだけです。

class something {そのため、 ...クラス定義を作成するときは、};継承したかどうかに関係なく、そのクラスに実装する予定のすべての関数を必ずリストしてください。変更せずに継承し、実装しない関数をリストしないでください。継承元がインターフェイスである場合 (すべての関数が純粋な仮想関数である場合)、それらをすべて実装する場合は、それらをすべてクラスにリストする必要があります。これは、言語がその状況の特別なケースを取り分けていないためです。これは、「私のヘッダーは祖父クラスのヘッダーファイルの正確なコピーでなければならない」とはまったく同等ではありません。祖父クラスにさらに多くの関数があり、それらを実装していない場合は、より明確にわかると思いますすべて子クラスにあります。

于 2013-03-15T14:26:21.493 に答える