2011-08-25 20 views
97

Tôi đã đi phỏng vấn xin việc hôm nay và được đưa ra câu hỏi thú vị này.Có thể trỏ đến điểm cơ sở đến một mảng các đối tượng có nguồn gốc không?

Bên cạnh rò rỉ bộ nhớ và thực tế không có dtor ảo, tại sao mã này gặp sự cố?

#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

Bên cạnh dấu chấm phẩy còn thiếu, ý bạn là gì? (Đó sẽ là một lỗi biên dịch thời gian, tuy nhiên, không phải thời gian chạy) –

+0

Bạn có chắc là tất cả đều là ảo không? –

+0

@Yochai, vâng tôi chắc chắn. –

Trả lời

148

Bạn không thể lập chỉ mục như vậy. Bạn đã phân bổ một mảng là Rectangles và được lưu trữ con trỏ đến dãy đầu tiên trong shapes. Khi bạn thực hiện shapes[1], bạn là hội nghị truyền hình (shapes + 1). Điều này sẽ không cung cấp cho bạn một con trỏ tới Rectangle tiếp theo, nhưng một con trỏ trỏ đến số Shape tiếp theo trong một mảng được giả định là 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 được may mắn và nhận được một vụ tai nạn.

Sử dụng con trỏ đến Rectangle giúp 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ó các loại khác nhau của Shape s trong mảng và sử dụng chúng polymorphically bạn cần một mảng của con trỏ để Shape.

+0

Có điều này là chính xác. :) –

13

Khi lập chỉ mục một con trỏ, trình biên dịch sẽ thêm số tiền thích hợp dựa trên kích thước của những gì nằm 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 (Rectangle) = 12 (số chính xác có thể sai).

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

+1

Là một chuyên gia không giỏi, đề cập đến SizeOf() đã giúp tôi hiểu những gì @R. Martinho đang nói trong câu trả lời của mình. –

36

Như Martinho Fernandes đã nói, việc lập chỉ mục sai. Nếu bạn muốn thay vì để lưu trữ một mảng của Shapes, bạn sẽ phải làm như vậy sử dụng một loạt các Shape * 's, như vậy:

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 làm thêm một bước khởi tạo các hình chữ nhật, kể từ khi khởi tạo các mảng chỉ thiết lập các con trỏ, và không phải là các đối tượng mình.

Các vấn đề liên quan