มีสองเทคนิคการจัดสรรหน่วยความจำที่ใช้กันอย่างแพร่หลาย: การจัดสรรอัตโนมัติและการจัดสรรแบบไดนามิก โดยทั่วไปมีพื้นที่หน่วยความจำที่สอดคล้องกันสำหรับแต่ละสแต็กและฮีป
ซ้อนกัน
สแต็กจะจัดสรรหน่วยความจำตามลำดับเสมอ สามารถทำได้เนื่องจากต้องการให้คุณปล่อยหน่วยความจำในลำดับย้อนกลับ (First-In, Last-Out: FILO) นี่เป็นเทคนิคการจัดสรรหน่วยความจำสำหรับตัวแปรโลคัลในภาษาการเขียนโปรแกรมจำนวนมาก มันเร็วมากเพราะต้องมีการทำบัญชีน้อยที่สุดและที่อยู่ถัดไปในการจัดสรรคือโดยปริยาย
ใน C ++ เรียกว่าหน่วยเก็บข้อมูลอัตโนมัติเนื่องจากหน่วยเก็บข้อมูลอ้างสิทธิ์โดยอัตโนมัติเมื่อสิ้นสุดขอบเขต ทันทีที่การดำเนินการบล็อกโค้ดปัจจุบัน (ใช้ตัวคั่น{}
) เสร็จสมบูรณ์หน่วยความจำสำหรับตัวแปรทั้งหมดในบล็อกนั้นจะถูกรวบรวมโดยอัตโนมัติ นี่เป็นช่วงเวลาที่ผู้ทำลายล้างถูกเรียกใช้เพื่อล้างทรัพยากร
กอง
ฮีปช่วยให้โหมดการจัดสรรหน่วยความจำมีความยืดหยุ่นมากขึ้น การทำบัญชีมีความซับซ้อนมากขึ้นและการจัดสรรช้าลง เนื่องจากไม่มีจุดปล่อยโดยปริยายคุณจะต้องปล่อยหน่วยความจำด้วยตนเองโดยใช้delete
หรือdelete[]
( free
ใน C) อย่างไรก็ตามการไม่มีจุดปล่อยโดยปริยายเป็นกุญแจสำคัญในความยืดหยุ่นของกอง
เหตุผลที่ใช้การจัดสรรแบบไดนามิก
แม้ว่าการใช้ฮีปจะช้าลงและอาจนำไปสู่การรั่วไหลของหน่วยความจำหรือการกระจายตัวของหน่วยความจำมีกรณีการใช้งานที่ดีอย่างสมบูรณ์แบบสำหรับการจัดสรรแบบไดนามิกเนื่องจากมีข้อ จำกัด น้อยกว่า
เหตุผลสำคัญสองข้อในการใช้การจัดสรรแบบไดนามิก:
คุณไม่ทราบว่าคุณต้องการหน่วยความจำในเวลารวบรวมเท่าใด ตัวอย่างเช่นเมื่ออ่านไฟล์ข้อความลงในสตริงคุณมักไม่รู้ขนาดไฟล์ที่มีดังนั้นคุณจึงไม่สามารถตัดสินใจได้ว่าจะจัดสรรหน่วยความจำเท่าใดจนกว่าคุณจะเรียกใช้โปรแกรม
คุณต้องการจัดสรรหน่วยความจำซึ่งจะคงอยู่หลังจากออกจากบล็อกปัจจุบัน ตัวอย่างเช่นคุณอาจต้องการเขียนฟังก์ชั่นstring readfile(string path)
ที่คืนค่าเนื้อหาของไฟล์ ในกรณีนี้แม้ว่าสแต็คสามารถเก็บเนื้อหาไฟล์ทั้งหมดคุณไม่สามารถกลับมาจากฟังก์ชั่นและเก็บบล็อกหน่วยความจำที่จัดสรร
ทำไมการจัดสรรแบบไดนามิกมักไม่จำเป็น
ใน C ++ มีโครงสร้างเรียบร้อยเรียกว่าdestructor กลไกนี้ช่วยให้คุณสามารถจัดการทรัพยากรโดยการปรับอายุการใช้งานของทรัพยากรให้สอดคล้องกับอายุการใช้งานของตัวแปร เทคนิคนี้เรียกว่าRAIIและเป็นจุดแยกของ C ++ มัน "ห่อ" ทรัพยากรลงในวัตถุ std::string
เป็นตัวอย่างที่สมบูรณ์แบบ ตัวอย่างนี้:
int main ( int argc, char* argv[] )
{
std::string program(argv[0]);
}
จริง ๆ แล้วจัดสรรจำนวนหน่วยความจำตัวแปร std::string
จัดสรรหน่วยความจำวัตถุโดยใช้กองและเผยแพร่ในเตาเผาของ ในกรณีนี้คุณไม่จำเป็นต้องจัดการทรัพยากรด้วยตนเองและยังคงได้รับประโยชน์จากการจัดสรรหน่วยความจำแบบไดนามิก
โดยเฉพาะอย่างยิ่งมันบอกเป็นนัยว่าในตัวอย่างนี้:
int main ( int argc, char* argv[] )
{
std::string * program = new std::string(argv[0]); // Bad!
delete program;
}
มีการจัดสรรหน่วยความจำแบบไดนามิกที่ไม่จำเป็น โปรแกรมต้องการการพิมพ์เพิ่มเติม (!) และแนะนำความเสี่ยงของการลืมจัดสรรหน่วยความจำ มันทำสิ่งนี้โดยไม่มีประโยชน์ชัดเจน
ทำไมคุณควรใช้ที่เก็บข้อมูลอัตโนมัติบ่อยที่สุดเท่าที่จะเป็นไปได้
โดยทั่วไปย่อหน้าสุดท้ายจะสรุปรวม การใช้ที่เก็บข้อมูลอัตโนมัติบ่อยที่สุดทำให้โปรแกรมของคุณ:
- พิมพ์เร็วขึ้น
- เร็วกว่าเมื่อวิ่ง
- การรั่วไหลของหน่วยความจำ / ทรัพยากรน้อย
คะแนนโบนัส
ในคำถามอ้างอิงมีข้อสงสัยเพิ่มเติม โดยเฉพาะคลาสต่อไปนี้:
class Line {
public:
Line();
~Line();
std::string* mString;
};
Line::Line() {
mString = new std::string("foo_bar");
}
Line::~Line() {
delete mString;
}
จริง ๆ แล้วมีความเสี่ยงที่จะใช้มากกว่าหนึ่งต่อไปนี้:
class Line {
public:
Line();
std::string mString;
};
Line::Line() {
mString = "foo_bar";
// note: there is a cleaner way to write this.
}
เหตุผลก็คือstd::string
กำหนดตัวสร้างสำเนาอย่างถูกต้อง พิจารณาโปรแกรมต่อไปนี้:
int main ()
{
Line l1;
Line l2 = l1;
}
เมื่อใช้เวอร์ชั่นดั้งเดิมโปรแกรมนี้อาจมีปัญหาเนื่องจากใช้delete
กับสตริงเดียวกันสองครั้ง การใช้เวอร์ชั่นที่แก้ไขแล้วแต่ละLine
อินสแตนซ์จะเป็นเจ้าของสตริงอินสแตนซ์ของตัวเองแต่ละตัวมีหน่วยความจำของตัวเองและทั้งคู่จะได้รับการปล่อยตัวเมื่อสิ้นสุดโปรแกรม
บันทึกอื่น ๆ
การใช้อย่างกว้างขวางของRAIIถือเป็นแนวปฏิบัติที่ดีที่สุดใน C ++ เนื่องจากเหตุผลทั้งหมดข้างต้น อย่างไรก็ตามมีประโยชน์เพิ่มเติมซึ่งไม่ชัดเจนในทันที โดยทั่วไปจะดีกว่าผลรวมของชิ้นส่วน กลไกทั้งหมดประกอบด้วย มันชั่ง
หากคุณใช้Line
คลาสเป็นแบบเอกสารสำเร็จรูป:
class Table
{
Line borders[4];
};
แล้วก็
int main ()
{
Table table;
}
จัดสรรสี่std::string
กรณีสี่Line
กรณีหนึ่งTable
อินสแตนซ์และทุกเนื้อหาสตริงและทุกอย่างเป็นอิสระโดยอัตโนมัติ