ตัวชี้ไปยังฐานสามารถชี้ไปยังอาร์เรย์ของวัตถุที่ได้รับหรือไม่?


99

วันนี้ฉันไปสัมภาษณ์งานและได้รับคำถามที่น่าสนใจนี้

นอกจากหน่วยความจำรั่วและไม่มี virtual 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

2
@ โทนี่ขอให้โชคดีแจ้งให้เราทราบ :)
Seth Carnegie

2
@AndreyT: ตอนนี้รหัสถูกต้องแล้ว (และถูกต้องด้วย) ->เป็นความผิดพลาดที่ทำโดยบรรณาธิการ
R.Martinho Fernandes

คำตอบ:


150

คุณไม่สามารถจัดทำดัชนีเช่นนั้นได้ คุณได้จัดสรรอาร์เรย์Rectanglesและจัดเก็บตัวชี้ไว้ที่ตัวชี้แรกในshapes. เมื่อคุณทำshapes[1]คุณ (shapes + 1)dereferencing นี้จะไม่ให้ชี้ไปข้างหน้าRectangleแต่ตัวชี้ไปยังสิ่งที่จะเป็นต่อไปในอาร์เรย์สันนิษฐานของShape Shapeแน่นอนว่านี่เป็นพฤติกรรมที่ไม่ได้กำหนด ในกรณีของคุณคุณโชคดีและประสบปัญหา

การใช้ตัวชี้เพื่อRectangleทำให้การจัดทำดัชนีทำงานได้อย่างถูกต้อง

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

หากคุณต้องการมีShapes ชนิดต่างๆในอาร์เรย์และใช้มันหลายชนิดคุณต้องมีอาร์เรย์ของพอยน์เตอร์ถึง Shape


37

ดังที่ Martinho Fernandes กล่าวว่าการจัดทำดัชนีไม่ถูกต้อง หากคุณต้องการจัดเก็บอาร์เรย์ของรูปร่างแทนคุณจะต้องทำโดยใช้อาร์เรย์ของ 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();
}

โปรดทราบว่าคุณต้องทำขั้นตอนเพิ่มเติมในการเตรียมใช้งาน Rectangle เนื่องจากการกำหนดค่าเริ่มต้นอาร์เรย์จะตั้งค่าตัวชี้เท่านั้นไม่ใช่วัตถุ


13

เมื่อสร้างดัชนีตัวชี้คอมไพลเลอร์จะเพิ่มจำนวนที่เหมาะสมตามขนาดของสิ่งที่อยู่ภายในอาร์เรย์ ดังนั้นบอกว่า sizeof (Shape) = 4 (เนื่องจากไม่มีตัวแปรสมาชิก) แต่ sizeof (สี่เหลี่ยมผืนผ้า) = 12 (ตัวเลขที่แน่นอนน่าจะผิด)

ดังนั้นเมื่อคุณทำดัชนีเริ่มต้นที่พูดว่า ... 0x0 สำหรับองค์ประกอบแรกจากนั้นเมื่อคุณพยายามเข้าถึงองค์ประกอบที่ 10 คุณกำลังพยายามไปยังที่อยู่ที่ไม่ถูกต้องหรือตำแหน่งที่ไม่ใช่จุดเริ่มต้นของวัตถุ


1
ในฐานะที่ไม่ใช่ผู้เชี่ยวชาญด้าน c ++ การกล่าวถึง SizeOf () ช่วยให้ฉันเข้าใจว่า @R คืออะไร Martinho กำลังพูดในคำตอบของเขา
Marjan Venema
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.