2009-05-03 15 views
3

派生クラスが配列で機能しないのはなぜですか? (C++)私は関数moveとVIRと呼ばれるクラスを、作成した

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

(それは変数とクラスから派生したのx、int型のY、およびchar型のSYM INT) は、私が得てきました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 sv1(0,0,'Q'); 
vir vir_RA[1]={sv1}; 

をsubvirを置くしかし、私はuのにしようとすると、 se sv1.move():

vir_RA [0] .move();

subvir move({x ++})ではなくvir move({})を使用します。私はsv1をvirとvir_RAをvirにしようとしましたが、それはうまく動作し、両方をsubvirにすると動作しますが、別のものにする必要があります。私はvir :: move()を純粋な仮想にしようとしましたが、配列を実体化するエラーが発生します。誰も私が配列からそれを使用するときに働くmove()を得る方法を知っていますか?

+0

初期化子は、メンバフィールドとベースオブジェクトを初期化するための好ましい方法です。詳細については、http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6を参照してください。例:subvir(int a、int b、char s):vir(a、b、s){} – outis

答えて

5

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

+0

ありがとうございました。 –

8

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

+0

スライシングはここでは起こりません。 move()は仮想関数ではないので、vir *はvir :: move()を呼び出します。 – jmucchiello

+2

@jmucchiello:ここでスライシングが行われています - vir_RAの最初の要素はコピー初期化されます、copy-construct)を、vir型のコンパイラーの自動生成コピーコンストラクターを使用してsv1から削除します。 –

5

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

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

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') }; 

あなたはまた、 fのDOはC++よくある質問Liteは、次のセクションをよく読んでください。

+1

最後のスニペットはエラーの原因です.2つの一時オブジェクトのアドレスを配列に格納しています。命令(セミコロン)の終わりまでに、両方の一時変数が破壊され、配列はポインタをどこにも保持しません。 –

+0

真。私が本当に気にしていたのは、 'new subvir ... 'でした - 私の答えを更新しました。 – dirkgently

2

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

+1

+1。多かれ少なかれ。もちろん、 "size(base_type)bytesをコピーする"というのは、デフォルト動作です。base_typeがユーザ定義のコピーコンストラクタを提供する場合、そのコンストラクタは "base_type const&"引数で呼び出され、derived_typeオブジェクトを参照しますイニシャライザーとして使用されています。 –

1

前の回答をまとめることができます。

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

第2の問題は多型です。使用時(move()への呼び出し時)、コンパイラはvir型のオブジェクトのmove()メソッドを呼び出していると考えます(配列はvirの配列なので)。 (コンパイラはもちろんスライシングのために考えているので、この場合は縮れます。)実際にあなたが意図したようにsubvirオブジェクトであれば、move()を呼び出すことによって呼び出されるsubvir :: move )virの仮想。

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

関連する問題