Tabana bir işaretçi bir türetilmiş nesneler dizisini işaret edebilir mi?


99

Bugün bir iş görüşmesine gittim ve bu ilginç soru verildi.

Bellek sızıntısı ve sanal yönlendirici olmaması gerçeğinin yanı sıra, bu kod neden çöküyor?

#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
Eksik noktalı virgül dışında, demek istiyorsun? (Bu, çalışma zamanı değil, derleme zamanı hatası olur)
Platinum Azure

Hepsinin sanal olduğuna emin misin?
Yochai Timmer

8
Bir Shape **Dikdörtgen dizisini işaret etmelidir . Daha sonra erişim şekiller [i] -> draw () olmalıdır;
RedX

2
@Tony iyi şanslar o zaman, bizi haberdar edin :)
Seth Carnegie

2
@AndreyT: Kod şu anda doğru (ve başlangıçta da doğruydu). ->Editör tarafından yapılan bir hataydı.
R. Martinho Fernandes

Yanıtlar:


150

Bu şekilde indeksleyemezsiniz. Rectanglesİlk giren için bir dizi ayırdınız ve bir gösterici depoladınız shapes. Bunu yaptığınızda shapes[1], referanstan kaldırıyorsunuz (shapes + 1). Bu size bir sonraki için bir işaretçi vermeyecek Rectangle, ancak Shapevarsayılan bir dizide bir sonraki olacağına dair bir işaretçi verecektir Shape. Elbette bu tanımlanmamış bir davranış. Senin durumunda, şanslısın ve kaza geçiriyorsun.

Rectangleİndekslemenin düzgün çalışmasını sağlamak için bir işaretçi kullanmak .

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

ShapeDizide farklı türlerde ' ler olmasını ve bunları polimorfik olarak kullanmak istiyorsanız , Shape için bir işaretçi dizisine ihtiyacınız vardır .


37

Martinho Fernandes'nin dediği gibi, endeksleme yanlış. Bunun yerine bir Şekiller dizisini depolamak isterseniz, bunu Shape * 'ler dizisi kullanarak yapmanız gerekir, örneğin:

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

Dikdörtgeni başlatmak için fazladan bir adım atmanız gerektiğini unutmayın, çünkü diziyi başlatmak yalnızca işaretçileri kurar, nesnelerin kendisini değil.


13

Bir işaretçiyi indekslerken, derleyici, dizinin içinde bulunan şeyin boyutuna göre uygun miktarı ekleyecektir. Diyelim ki sizeof (Shape) = 4 (üye değişkeni olmadığı için). Ancak sizeof (Dikdörtgen) = 12 (kesin sayılar muhtemelen yanlıştır).

Yani, ilk eleman için ... 0x0'dan başlayarak indekslediğinizde, 10. elemente erişmeye çalıştığınızda, geçersiz bir adrese veya nesnenin başlangıcı olmayan bir konuma gitmeye çalışıyorsunuz.


1
C ++ olmayan bir usta olarak, SizeOf () 'dan bahsetmek @R'nin ne olduğunu anlamama yardımcı oldu. Martinho cevabında diyordu.
Marjan Venema
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.