ベースへのポインタは派生オブジェクトの配列を指すことができますか?


99

今日は就職の面接に行って、この興味深い質問を受けました。

メモリリークと仮想dtorがないという事実に加えて、このコードがクラッシュするのはなぜですか?

#include <iostream>

//besides the obvious mem leak, why does this code crash?

class Shape
{
public:
    virtual void draw() const = 0;
};

class Circle : public Shape
{
public:
    virtual void draw() const { }

    int radius;
};

class Rectangle : public Shape
{
public:
    virtual void draw() const { }

    int height;
    int width;
};

int main()
{
    Shape * shapes = new Rectangle[10];
    for (int i = 0; i < 10; ++i)
        shapes[i].draw();
}

1
欠落しているセミコロンの他に、どういう意味ですか?(ただし、ランタイムではなく、コンパイル時エラーになります)
Platinum Azure

それらはすべて仮想でしたか?
Yochai Timmer

8
Shape **長方形の配列を指している必要があります。次に、アクセスは形状[i]-> draw();である必要があります。
RedX 2011

2
@Tony幸運、それでは私たちに知らせてください:)
セス・カーネギー

2
@AndreyT:コードは正しくなりました(元々は正しいものでした)。これ->は編集者が犯した間違いでした。
R.マルティーニョフェルナンデス

回答:


150

そのように索引付けすることはできません。の配列を割り当てRectangles、最初のポインタをに保存しましたshapes。あなたがそうshapes[1]するとき、あなたは逆参照しています(shapes + 1)。これは、次へのポインタを提供しませんが、推定される配列のRectangle次のものへのポインタを提供します。もちろん、これは未定義の動作です。あなたの場合、あなたは幸運でクラッシュしている。ShapeShape

ポインターを使用しRectangleて、索引付けを正しく機能させます。

int main()
{
   Rectangle * shapes = new Rectangle[10];
   for (int i = 0; i < 10; ++i) shapes[i].draw();
}

Shape配列にさまざまな種類のを入れてそれらを多態的に使用する場合は、Shapeへのポインターの配列が必要です。


37

Martinho Fernandesが言ったように、索引付けは間違っています。代わりにShapesの配列を格納する場合は、次のようにShape *の配列を使用して格納する必要があります。

int main()
{
   Shape ** shapes = new Shape*[10];
   for (int i = 0; i < 10; ++i) shapes[i] = new Rectangle;
   for (int i = 0; i < 10; ++i) shapes[i]->draw();
}

配列の初期化ではポインタのみが設定され、オブジェクト自体は設定されないため、四角形を初期化する追加の手順を実行する必要があることに注意してください。


13

ポインターにインデックスを付けるとき、コンパイラーは配列内にあるもののサイズに基づいて適切な量を追加します。したがって、sizeof(Shape)= 4(メンバー変数がないため)とします。しかし、sizeof(Rectangle)= 12(正確な数値はおそらく間違っています)。

したがって、たとえば最初の要素の0x0からインデックスを作成する場合、10番目の要素にアクセスしようとすると、無効なアドレスまたはオブジェクトの先頭ではない場所に移動しようとしています。


1
非c ++の熟達者として、SizeOf()について言及すると、@ Rの内容を理解するのに役立ちました。マルティーニョは彼の答えで言っていた。
Marjan Venema、2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.