Một con trỏ đến cơ sở có thể trỏ đến một mảng các đối tượng dẫn xuất không?


99

Tôi đã đến một cuộc phỏng vấn việc làm ngày hôm nay và được đưa ra câu hỏi thú vị này.

Ngoài việc bộ nhớ bị rò rỉ và thực tế là không có dtor ảo, tại sao đoạn mã này lại bị lỗi?

#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
Ngoài dấu chấm phẩy bị thiếu, ý bạn là? (Đó sẽ là một lỗi thời gian biên dịch, tuy nhiên, không phải thời gian chạy)
Platinum Azure

Bạn có chắc tất cả đều là ảo?
Yochai Timmer

8
Nó phải là Shape **Nó đang trỏ đến một mảng Hình chữ nhật. Khi đó, quyền truy cập đáng lẽ phải là các hình [i] -> draw ();
RedX

2
@Tony chúc may mắn sau đó, hãy tiếp tục thông báo cho chúng tôi :)
Seth Carnegie vào

2
@AndreyT: Hiện tại mã đã chính xác (và ban đầu cũng chính xác). Đó ->là một sai lầm của một biên tập viên.
R. Martinho Fernandes

Câu trả lời:


150

Bạn không thể lập chỉ mục như vậy. Bạn đã cấp phát một mảng Rectanglesvà lưu trữ một con trỏ đến đầu tiên trong shapes. Khi bạn làm shapes[1]bạn đang tham khảo (shapes + 1). Điều này sẽ không cung cấp cho bạn một con trỏ đến cái tiếp theo Rectangle, nhưng một con trỏ đến cái gì sẽ là cái tiếp theo Shapetrong một mảng giả định của Shape. Tất nhiên, đây là hành vi không xác định. Trong trường hợp của bạn, bạn đang gặp may và gặp tai nạn.

Sử dụng con trỏ để Rectanglelàm cho việc lập chỉ mục hoạt động chính xác.

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

Nếu bạn muốn có nhiều loại Shapes khác nhau trong mảng và sử dụng chúng một cách đa hình, bạn cần một mảng con trỏ tới Hình dạng.


37

Như Martinho Fernandes đã nói, việc lập chỉ mục là sai. Thay vào đó, nếu bạn muốn lưu trữ một mảng Hình dạng, bạn sẽ phải làm như vậy bằng cách sử dụng một mảng Hình dạng *, như sau:

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

Lưu ý rằng bạn phải thực hiện thêm một bước khởi tạo Hình chữ nhật, vì việc khởi tạo mảng chỉ thiết lập các con trỏ chứ không phải bản thân các đối tượng.


13

Khi lập chỉ mục một con trỏ, trình biên dịch sẽ thêm số lượng thích hợp dựa trên kích thước của những gì nằm bên trong mảng. Vì vậy, nói rằng sizeof (Shape) = 4 (vì nó không có biến thành viên). Nhưng sizeof (Hình chữ nhật) = 12 (số chính xác có thể sai).

Vì vậy, khi bạn lập chỉ mục bắt đầu từ nói ... 0x0 cho phần tử đầu tiên, sau đó khi bạn cố gắng truy cập phần tử thứ 10, bạn đang cố gắng đi đến một địa chỉ không hợp lệ hoặc một vị trí không phải là phần đầu của đối tượng.


1
Là một người không thành thạo c ++, việc đề cập đến SizeOf () đã giúp tôi hiểu @R là gì. Martinho đã nói trong câu trả lời của mình.
Marjan Venema,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.