質問

関数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()を機能させる方法を知っている人はいますか?

役に立ちましたか?

解決

この場合、インスタンスの配列ではなく、ポインターの配列が必要です。 vir []

の代わりにvir * []を使用します

他のヒント

スライスという問題に直面しています。ポインターの配列、または Boost.ptr_containerのようなものを使用します。

基本クラスには、必要なものを取得するための 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') };

また、f C ++ FAQ Liteの以下のセクションを読んでください:

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

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

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

以前の回答を統合します。

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

2番目の問題は多態性です。使用の時点(move()の呼び出し)で、コンパイラは、タイプがvirのオブジェクトのmove()メソッドを呼び出していると判断します(配列はvirの配列であるため)。 (コンパイラーはもちろん、正しいため、スライスのため、この場合のように縮退します。)意図したとおりに実際にsubvirオブジェクトであった場合、move()を呼び出してsubvir :: move()を呼び出すことができます。 )virで仮想

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top