มีความแตกต่างที่สำคัญระหว่างทั้งสอง
ทุกอย่างไม่ได้จัดสรรที่มีnew
พฤติกรรมเหมือนประเภทค่าใน C # (และคนมักจะบอกว่าวัตถุเหล่านั้นได้รับการจัดสรรในกองซึ่งน่าจะเป็นเรื่องธรรมดา / กรณีที่เห็นได้ชัดมากที่สุด แต่ไม่เป็นความจริงเสมอ. อีกอย่างแม่นยำวัตถุจัดสรรโดยไม่ต้องใช้new
มีการจัดเก็บข้อมูลโดยอัตโนมัติ ระยะเวลา
ทุกสิ่งที่จัดสรรด้วยnew
จะถูกจัดสรรในฮีปและตัวชี้จะถูกส่งคืนเหมือนกับประเภทการอ้างอิงใน C #
สิ่งใดก็ตามที่ถูกจัดสรรบนสแต็กจะต้องมีขนาดคงที่กำหนดที่การคอมไพล์เวลา (คอมไพเลอร์ต้องตั้งค่าตัวชี้สแต็กอย่างถูกต้องหรือถ้าวัตถุเป็นสมาชิกของคลาสอื่นจะต้องปรับขนาดของคลาสอื่นนั้น) . นั่นเป็นเหตุผลที่อาร์เรย์ใน C # เป็นประเภทอ้างอิง พวกเขาจะต้องเป็นเพราะประเภทการอ้างอิงเราสามารถตัดสินใจที่รันไทม์จำนวนหน่วยความจำที่จะขอ และเช่นเดียวกับที่นี่ เฉพาะอาร์เรย์ที่มีขนาดคงที่ (ขนาดที่สามารถกำหนดได้ในเวลาคอมไพล์) สามารถจัดสรรด้วยระยะเวลาการจัดเก็บอัตโนมัติ (บนสแต็ก) new
อาร์เรย์ขนาดแบบไดนามิกจะต้องได้รับการจัดสรรในกองโดยโทร
(และนั่นคือสิ่งที่คล้ายคลึงกับ C # หยุด)
ตอนนี้สิ่งใดก็ตามที่ถูกจัดสรรบนสแต็กมีระยะเวลาการจัดเก็บ "อัตโนมัติ" (คุณสามารถประกาศตัวแปรได้auto
แต่นี่คือค่าเริ่มต้นหากไม่มีการระบุประเภทการจัดเก็บอื่น ๆ ดังนั้นคำหลักนั้นไม่ได้ใช้จริง ๆ มาจาก)
ระยะเวลาการจัดเก็บอัตโนมัติหมายถึงสิ่งที่ดูเหมือนว่าระยะเวลาของตัวแปรจะถูกจัดการโดยอัตโนมัติ ในทางตรงกันข้ามคุณต้องลบสิ่งใด ๆ ที่จัดสรรในฮีปด้วยตนเอง นี่คือตัวอย่าง:
void foo() {
bar b;
bar* b2 = new bar();
}
ฟังก์ชันนี้สร้างสามค่าที่ควรพิจารณา:
ในบรรทัดที่ 1 มันประกาศตัวแปรb
ประเภทbar
บนสแต็ค (ระยะเวลาอัตโนมัติ)
ในบรรทัดที่ 2 มันจะประกาศbar
ตัวชี้b2
บนสแต็ค (ระยะเวลาอัตโนมัติ) และเรียกใหม่การจัดสรรbar
วัตถุบนฮีป (ระยะเวลาแบบไดนามิก)
เมื่อฟังก์ชั่นกลับมาสิ่งต่อไปนี้จะเกิดขึ้น: อันดับแรกb2
ออกนอกขอบเขต (คำสั่งของการทำลายอยู่ตรงข้ามกับลำดับการก่อสร้างเสมอ) แต่b2
เป็นเพียงพอยน์เตอร์ดังนั้นจึงไม่มีอะไรเกิดขึ้น และที่สำคัญหน่วยความจำที่ชี้ไปยัง ( bar
อินสแตนซ์ของฮีป) ไม่ได้ถูกแตะต้อง เฉพาะตัวชี้เท่านั้นที่เป็นอิสระเนื่องจากตัวชี้เท่านั้นที่มีช่วงเวลาอัตโนมัติ ข้อที่สองb
ออกนอกขอบเขตดังนั้นเนื่องจากมีระยะเวลาอัตโนมัติตัวทำลายของมันจะถูกเรียกใช้และหน่วยความจำจะถูกปล่อยให้เป็นอิสระ
และbar
ตัวอย่างบนกองเหรอ? มันอาจจะยังคงอยู่ที่นั่น ไม่มีใครใส่ใจลบดังนั้นเราจึงรั่วหน่วยความจำ
จากตัวอย่างนี้เราจะเห็นได้ว่าสิ่งใดก็ตามที่มีระยะเวลาอัตโนมัติรับประกันว่าจะมี destructor ที่เรียกว่าเมื่อออกนอกขอบเขต นั่นมีประโยชน์ แต่สิ่งใดก็ตามที่จัดสรรในฮีปจะคงอยู่ตราบใดที่เราต้องการและสามารถกำหนดขนาดแบบไดนามิกเช่นเดียวกับในกรณีของอาร์เรย์ นั่นก็มีประโยชน์ เราสามารถใช้สิ่งนั้นเพื่อจัดการการจัดสรรหน่วยความจำของเรา จะเกิดอะไรขึ้นถ้าคลาส Foo จัดสรรหน่วยความจำบางส่วนบนฮีปในตัวสร้างและลบหน่วยความจำนั้นในตัวทำลาย จากนั้นเราจะได้รับสิ่งที่ดีที่สุดทั้งสองโลกการจัดสรรหน่วยความจำที่ปลอดภัยซึ่งรับประกันว่าจะได้รับการปลดปล่อยอีกครั้ง แต่ไม่มีข้อ จำกัด ในการบังคับให้ทุกอย่างอยู่ในสแต็ก
และนั่นเป็นวิธีที่รหัส C ++ ส่วนใหญ่ทำงานได้อย่างแท้จริง ดูstd::vector
ตัวอย่างของไลบรารี่มาตรฐาน โดยทั่วไปจะถูกจัดสรรในสแต็ก แต่สามารถปรับขนาดและปรับขนาดแบบไดนามิก และทำได้โดยการจัดสรรหน่วยความจำภายในฮีปตามความจำเป็น ผู้ใช้ในชั้นเรียนไม่เคยเห็นสิ่งนี้ดังนั้นจึงไม่มีโอกาสรั่วหน่วยความจำหรือลืมล้างสิ่งที่คุณจัดสรรไว้
หลักการนี้เรียกว่า RAII (การรับทรัพยากรคือการเริ่มต้น) และสามารถขยายไปยังทรัพยากรใด ๆ ที่ต้องได้รับและเผยแพร่ (ซ็อกเก็ตเครือข่ายไฟล์การเชื่อมต่อฐานข้อมูลล็อคการซิงโครไนซ์) พวกมันทั้งหมดสามารถหาได้ในนวกรรมิกและปล่อยใน destructor ดังนั้นคุณจึงมั่นใจได้ว่าทรัพยากรทั้งหมดที่คุณได้รับจะได้รับอิสระอีกครั้ง
ตามกฎทั่วไปห้ามใช้ใหม่ / ลบโดยตรงจากรหัสระดับสูงของคุณ ห่อไว้ในคลาสที่สามารถจัดการหน่วยความจำสำหรับคุณได้และจะทำให้แน่ใจได้ว่าจะได้รับการปลดปล่อยอีกครั้ง (ใช่อาจจะมีข้อยกเว้นกฎนี้. โดยเฉพาะอย่างยิ่งตัวชี้สมาร์ทต้องการให้คุณโทรnew
ได้โดยตรงและผ่านตัวชี้ไปยังคอนสตรัคซึ่งจากนั้นจะใช้เวลามากกว่าและสร้างความมั่นใจdelete
จะเรียกว่าได้อย่างถูกต้อง. แต่ตอนนี้ก็ยังคงเป็นกฎที่สำคัญมากของนิ้วหัวแม่มือ )