3

関数 move を使用して、vir という名前のクラスを作成しました。

class vir
{
public:
     vir(int a,int b,char s){x=a;y=b;sym=s;}
     void move(){}
};

(変数 int x、int y、および char sym を持つクラスから派生したものです) これから、subvir と呼ばれるクラスを派生させました。

class subvir:public vir
{
public:
     subvir(int a,int b,char s){x=a;y=b;sym=s;}
     void move();
};
subvir::move()
{
     x++;
     return;
}

次に、vir の配列を作成し、そこに subvir を入れました。

subvir sv1(0,0,'Q');
vir vir_RA[1]={sv1};

しかし、sv1.move() を使用しようとすると:

vir_RA[0].move();

subvir move ({x++}) ではなく、vir move ({}) を使用します。sv1 を vir に、vir_RA を vir にしようとしましたが、動作し、両方を subvir にすると動作しますが、異なる必要があります。vir::move() を純粋な仮想にしようとしましたが、配列を実証するエラーが発生します。配列から使用するときに move() を機能させる方法を知っている人はいますか?

4

6 に答える 6

8

基本クラスには、必要virtualなものを取得するための関数が必要です。これらを純粋にすると、インスタンス化できない抽象基本クラスになります。ただし、抽象基本クラスへのポインター/参照を作成し、それらに派生クラス オブジェクトを割り当てることはできます。基本クラスは次のように表すのが最適です。

class vir
{
public:
     vir(int a,int b,char s){x=a;y=b;sym=s;}
     virtual void move(){}
};

これにより、派生クラスmoveも仮想化されます。ただし、move定義には戻り値がなく、コンパイルされません。試す:

void subvir::move()
{
     x++;
     return;
}

動的バインディングが機能するには、ポインター (他の回答で述べたように) または派生クラスへの参照が必要であることに注意してください。したがって、virオブジェクトの配列の代わりに、基底クラス ポインターの配列を使用します。

vir* v[ 2 ] = { new subvir(0, 0, 'Q'), new subvir(10, -10, 'P') };

C++ FAQ Lite の次のセクションも読んでください。

于 2009-05-03T06:55:27.787 に答える
8

スライスと呼ばれる問題が発生しています。ポインターの配列、またはBoost.ptr_containerのようなものを使用します。

于 2009-05-03T06:41:12.410 に答える
5

この場合、インスタンスの配列ではなく、ポインターの配列が必要です。vir[] の代わりに vir*[] を使用する

于 2009-05-03T06:38:10.497 に答える
5

2つのこと。配列は vir の配列なので、もちろん vir::move を使用します。move() は仮想メソッドではありません。

しかし、もっと重要なのはスライスです。サブクラスを配列に入れることはできません。sizeof vir != sizeof subvir の場合、アレイは正しく整列しません。現在、それらは同じサイズです。しかし、そうでない場合はどうなりますか。

于 2009-05-03T06:42:57.850 に答える
2

はい、基本的にコンパイラは配列内のサブクラスを許可しません。配列は型サイズに対して厳密に初期化され、サブタイプは親よりも大きくなる傾向があり、サブタイプ値で配列を初期化できると問題が発生するためです。実際に起こることは、コンパイラが最初に配列 N * size(base_type) バイトを割り当てることです。そして、各初期化オブジェクトの size(base_type) バイトをコピーします。それらが異なるタイプである場合、それらは切り捨てられ、コードで奇妙なことが起こる可能性があります.

于 2009-05-03T07:27:00.567 に答える
1

以前の回答を統合させてください。

ここには実際には 2 つの問題があります。1つはスライスです。subvir のコピーを使用して virs の配列を初期化しています。このような場合、コンパイラは vir 部分を subvir からスライスして配列にコピーするため、実際にはそこに vir オブジェクトのみを取得します。あなたの特定のケースでは、subvir には vir 以外の追加のデータ メンバーがないため、スライスはやや縮退しており、vir オブジェクトは subvir オブジェクトによく似ています。ただし、vir と subvir は異なるクラスであり、配列内のオブジェクトは vir オブジェクトであり、vir を装った subvir オブジェクトではありません。両者の違いが実際に明らかになる 1 つの方法は、両方が同じデータ メンバーを持っていたとしても、vir が subvir によってオーバーロードされた仮想関数を持っていた場合です。その場合、配列内のオブジェクトの vtable ポインターは、subvir ではなく、vir の vtable を指します。もちろん、

2 つ目の問題はポリモーフィズムです。使用の時点 (move() の呼び出し) で、コンパイラは、タイプ vir のオブジェクトの move() メソッドを呼び出していると見なします (配列は virs の配列であるため)。(コンパイラはもちろん、スライシングのために、この場合のように縮退すると考えるのは正しいです。)実際に意図したとおりに subvir オブジェクトであった場合、move( を作成することで subvir::move() を呼び出すことができます。 ) vir で仮想。

目的の動作を得るには、ポインターの配列を使用できます (ただし、最初にコピーを作成し、コピーへのポインターで配列を初期化しない限り、そのコピーではなく、sv1 を直接操作することになります)。

于 2009-05-03T09:27:55.923 に答える