Bisakah pointer ke titik dasar ke array objek turunan?


99

Saya pergi ke wawancara kerja hari ini dan diberi pertanyaan menarik ini.

Selain kebocoran memori dan fakta tidak ada dtor virtual, mengapa kode ini macet?

#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
Selain titik koma yang hilang, maksud Anda? (Itu akan menjadi kesalahan waktu kompilasi, bukan runtime)
Platinum Azure

Apakah Anda yakin semuanya virtual?
Yochai Timmer

8
Seharusnya Shape **Itu menunjuk ke array Persegi Panjang. Kemudian aksesnya haruslah bentuk [i] -> draw ();
RedX

2
@Tony semoga sukses, terus beri tahu kami :)
Seth Carnegie

2
@AndreyT: Kode sekarang sudah benar (dan awalnya juga benar). Itu ->adalah kesalahan yang dibuat oleh editor.
R. Martinho Fernandes

Jawaban:


150

Anda tidak dapat mengindeks seperti itu. Anda telah mengalokasikan larik Rectanglesdan menyimpan penunjuk ke in shapes. Pertama . Saat Anda melakukannya, shapes[1]Anda akan melakukan dereferensi (shapes + 1). Ini tidak akan memberi Anda penunjuk ke yang berikutnya Rectangle, tetapi penunjuk ke apa yang akan menjadi berikutnya Shapedalam larik yang dianggap Shape. Tentu saja, ini adalah perilaku yang tidak terdefinisi. Dalam kasus Anda, Anda sedang beruntung dan mengalami kecelakaan.

Menggunakan penunjuk untuk Rectanglemembuat pengindeksan bekerja dengan benar.

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

Jika Anda ingin memiliki berbagai jenis Shapes dalam larik dan menggunakannya secara polimorfis, Anda memerlukan larik penunjuk ke Bentuk.


37

Seperti yang dikatakan Martinho Fernandes, pengindeksannya salah. Jika Anda ingin menyimpan larik Bentuk, Anda harus melakukannya menggunakan larik Bentuk *, seperti ini:

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

Perhatikan bahwa Anda harus melakukan langkah tambahan untuk menginisialisasi Persegi Panjang, karena menginisialisasi array hanya menyiapkan penunjuk, dan bukan objek itu sendiri.


13

Saat mengindeks sebuah pointer, compiler akan menambahkan jumlah yang sesuai berdasarkan ukuran dari apa yang ada di dalam array. Jadi katakanlah sizeof (Shape) = 4 (karena tidak memiliki variabel anggota). Tapi sizeof (Rectangle) = 12 (angka pastinya kemungkinan besar salah).

Jadi ketika Anda mengindeks mulai dari katakanlah ... 0x0 untuk elemen pertama, kemudian ketika Anda mencoba mengakses elemen ke-10 Anda mencoba untuk pergi ke alamat yang tidak valid atau lokasi yang bukan awal dari objek.


1
Sebagai seorang non c ++ mahir, menyebutkan SizeOf () membantu saya memahami apa itu @R. Martinho mengatakan dalam jawabannya.
Marjan Venema
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.