24.หน่วยความจำแบบไดนามิก

คัดลอกมาจาก:::::::http://marcuscode.com/lang/cpp/dynamic-memory
ในบทก่อนหน้าของบทเรียนนี้ คุณได้เห็นแล้วว่าหน่วยความจำต้องถูกจองก่อนสำหรับตัวแปร ก่อนที่โปรแกรมจะรัน ในบางกรณี เราอาจจะต้องการจองหน่วยความจำแบบไดนามิกส์ในเวลาที่โปรแกรมรัน
ในบทนี้ การจองหน่วยความจำแบบไดนามิส์ ในภาษา C++ นั้นให้เราสามารถจัดการกับหน่วยความจำได้ในเวลาที่โปรแกรมทำงาน เช่น การจองหน่วยความจำ และการคืนหน่วยความจำให้กับระบบ

Operators new and new[]

เราสามารถจัดสรรหน่วยความจำแบบไดนามิกโดยการใช้คำสั่ง new สำหรับตัวแปรปกติ และ new[] สำหรับอาเรย์ นี่จะส่งค่าพอยน์เตอร์ที่พอยน์เตอร์ชี้ไปยังตำแหน่งเริ่มต้นของตัวแปร คุณสามารถทำตามรูปแบบของมันได้ดังนี้:
int * mypointer;
mypointer = new int [5];
ในตัวอย่าง การจัดสรรหน่วยความจำแบบไดนามิกสำหรับตัวแปรแบบ integer 5 ตัวแปร ตอนแรกเราประกาศ mypointer ซึ่งประเภทของมันต้องตรงกันกับค่าของข้อมูลที่เราจะจัดสรรหน่วยความจำให้, int อันที่สองเราใช้คำสั่ง new เพื่อจัดสรรหน่วยความจำที่มีขนาด 5 และใส่ขนาดภายในวงเล็บ [] ซึ่งสามารถเป็นจำนวนเต็มบวก หลังจากนั้นพอยน์เตอร์จะชี้ไปยังตำแหน่งแรกของหน่วยความจำที่ได้จองไว้แล้ว

Operators delete and delete[]

โดยปกติ โปรแกรมจะคืนค่าหน่วยความจำให้ระบบหลังจากที่โปรแกรมสิ้นสุดการทำงานอย่างสมบูรณ์ แต่บางครั้งเราได้เสร็จสิ้นงานของเราขณะเดียวกันโปรแกรมยังไม่จบ เราสามารถคืนค่าหน่วยความจำที่เราได้จองไว้โดยการใช้คำสั่ง delete:
delete mypointer;
delete[] mypointer;
บรรทักแรกเราคืนค่าหน่วยความจำสำหรับตัวแปร และบรรทัดที่สองสำหรับอาเรย์ หลังจากนั้นหน่วยความจำจะคืนสู่ระบบและสามารถนำไปใช้อย่างอื่นได้
มาดูตัวอย่างกับการใช้การจองหน่วยความจำแบบไดนามิก
#include <iostream>
#include <new>

using namespace std;

int main()
{
    int i,n;
    int * p;
    cout << "How much memory would you like to allocate? ";
    cin >> n;
    p= new (nothrow) int[n];
    if (p == NULL)
    {
        cout << "Error: memory could not be allocated";
    }
    else
    {
        for (i = 0; i < n; i++)
        {
            cout << "Enter p[" << i << "]: ";
            cin >> p[i];
        }
        cout << "You have entered: ";
        for (i = 0; i < n; i++)
        {
            cout << p[i] << ", ";
        }
        delete[] p;
    }
    return 0;
}

Output

How much memory would you like to allocate? 5
Enter number for p[0]: 1
Enter number for p[1]: 2
Enter number for p[2]: 3
Enter number for p[3]: 4
Enter number for p[4]: 5
You have entered: 1, 2, 3, 4, 5,
ในตัวอย่างนี้ เราได้เขียนโปรมแบบไดนามิกที่จะถามถึงขนาดที่เราต้องการสร้าง หลังจากนั้นเราจะจองหน่วยความจำแบบไดนามิกกับขนาดที่ใส่เข้ามา หน่วยความจำแบบไดนามิกนั้นเหมือนอาเรย์ ซึ่งมันเป็นลำดับของหน่วยความจำ เมื่อเราเสร็จสิ้นงานของเรา เราจะคืนค่าหน่วยความจำที่เราได้จองไว้

Dynamic array Vs. Array

ในตัวอย่างก่อนหน้า คุณจะเห็นว่าตัวแปรที่เราสร้างนั้นเหมือนกับอาเรย์ แน่นอนว่ามันคืออาเรย์ แต่เป็นอาเรย์แบบไดนามิกส์หรือพอยน์เตอร์อาเรย์ อาเรย์ประเภทนี้แตกต่างจากอาเรย์ปกติก็คือการเก็บข้อมูลในหน่วยความจำ ซึ่งในการใช้งานอาเรย์แบบปกติของเรานั้นจะต้องประกาศขนาดของอาเรย์ให้เท่ากับขนาดสูงสุดของข้อมูลที่จะมีได้ในอาเรย์ก่อน ทำให้หน่วยความจำทั้งหมดถูกจองไว้ แต่สำหรับไดนามิกส์อาเรย์นั้นจะใช้พื้นที่หน่วยความจำเท่ากับขนาดของข้อมูลจริงๆ ที่มีอยู่ มาดูตัวอย่าง
#include <iostream>

using namespace std;

int main()
{
    const int SIZE = 10;

    // formal array
    int number[SIZE];

    // dynamic array
    int * mypointer;
    mypointer = new int [SIZE];

    cout << "Empty arrays" << endl;
    cout << "Size dynamic array = "
         << sizeof(int) * 0 + sizeof(mypointer) << endl;
    cout << "Size array = " << sizeof(number) << endl;

    for (int i = 0; i < 3; i++)
    {
        mypointer[i] = 1;
        number[i] = 1;
    }

    cout << "\nAdded 3 items to arrays" << endl;
    cout << "Size dynamic array = "
         << sizeof(int) * 3 + sizeof(mypointer) << endl;
    cout << "Size array = " << sizeof(number) << endl;

    for (int i = 3; i < 10; i++)
    {
        mypointer[i] = 1;
        number[i] = 1;
    }

    cout << "\nAdded 10 items to arrays" << endl;
    cout << "Size dynamic array = "
         << sizeof(int) * 10 + sizeof(mypointer) << endl;
    cout << "Size array = " << sizeof(number) << endl;

    delete[] mypointer;

    return 0;
}
ในตัวอย่าง เป็นโปรแกรมในการแสดงผลความแตกต่างระหว่างไดนามิกส์อาเรย์และอาเรย์แบบปกติ ในตอนแรกของโปรแกรมเราได้สร้างอาเรย์ขนาดปกติสำหรับเก็บตัวเลขแบบ Integer จำนวน 10 ตัว หลังจากนั้นสร้างไดนามิกส์อาเรย์สำหรับเก็บตัวเลขเช่นกัน
// formal array
int number[SIZE];

// dynamic array
int * mypointer;
mypointer = new int [SIZE];
ในการสร้างอาเรย์ทั้งสองแบบ ในอาเรย์ปกติโปรแกรมจะจองหน่วยความจำสำหรับ 10 ตัวเลขในตอนที่สร้างทันที และสำหรับไดนามิกส์อาเรย์โปรแกรมจะจองเพียงหน่วยความจำของตัวแปรพอยน์เตอร์ที่ชี้ไปยังตำแหน่งแรก (ส่วนหัว) ของเรย์ หลังจากนั้นเราได้แสดงขนาดของหน่วยความจำจริงๆ ที่ใช้เก็บข้อมูลของอาเรย์ทั้งสองแบบโดยใช้ฟังก์ชัน sizeof() สำหรับหาขนาดของออบเจ็ค และนี่เป็นผลลัพธ์ของโปรแกรม
Empty arrays
Size dynamic array = 4
Size array = 40

Added 3 items to arrays
Size dynamic array = 16
Size array = 40

Added 10 items to arrays
Size dynamic array = 44
Size array = 40
จากผลลัพธ์ของโปรแกรม แสดงให้คุณเห็นว่าขนาดของการเก็บแบบอาเรย์ปกติสำหรับตัวเลข 10 ตัวนั้นจะคงที่เสมอ ซึ่งเป็นขนาดสูงสุดที่เราได้จองไว้ในอาเรย์ ส่วนขนาดของหน่วยความจำที่ใช้ในไดนามิกส์อาเรย์นั้นจะเพิ่มขึ้นตามจำนวนของข้อมูลที่มีอยู่ และจะบวกเพิ่มขึ้นอีก 4 bytes สำหรับตัวแปรพอยน์ของอาเรย์ ดังนั้น คุณพอจะเห็นประสิทธิภาพของโปรแกรมว่าวิธีไหนจะดีกว่าถ้าหากเราต้องการเขียนโปรแกรมให้ประหยัดหน่วยความจำใน run-time
ในบทนี้ คุณได้เรียนรู้เกี่ยวกับประเภทข้อมูลแบบไดนามิกส์ในภาษา C++ สำหรับเก็บข้อมูลในหน่วยความจำให้มีประสิทธิภาพมากขึ้นจากอาเรย์แบบปกติ อย่างไรก็ตามในการใช้งานจริง คุณควรจะพิจารณาโปรแกรมและความจำเป็นของคุณในการใช้งานแต่ละแบบ และสิ่งที่สำคัญของบทนี้คุณได้รู้ข้อแตกต่างของพวกเขา
*****************************************************************************************************************************************
ในโปรแกรมทุกโปรแกรมไม่ว่าในภาษาใดๆก็ตามนะครับ เมื่อเราทำการประกาศตัวแปรขึ้นมา เครื่องคอมพิวเตอร์ของเราจะทำการจับจองเนื้อที่หน่วยความจำของตัวแปรนั้นครับ เช่น ถ้าเราประกาศว่า
int x;
เครื่องคอมพิวเตอร์จะทำการจองหน่วยความจำขนาด 4 ไบต์(สำหรับข้อมูลชนิด int) ที่จะใช้เก็บตัวแปร x
ตัวแปรที่จองเนื้อที่ในหน่วยความจำ จะจองไว้อย่างนั้นจนกว่าเราจะออกจากฟังก์ชันนั้นๆ ครับ เช่น ถ้าเราสั่ง int x; ในฟังก์ชัน main ตัวแปร x ก็จะคงอยู่อย่างนั้นจนกระทั่งสิ้นสุดการทำงานของโปรแกรม(กล่าวคือออกจาก main) หรือ สมมติว่าเราประกาศ int y; ในฟังก์ชัน myFunction เมื่อเรามีการเรียกใช้ myFunction เครื่องคอมพิวเตอร์จะจองหน่วยความจำสำหรับตัวแปร y เมื่อการทำงานของ myFunction สิ้นสุดลงก็จะคืนหย่วยความจำที่จองให้กับตัวแปร y คืนให้กับคอมพิวเตอร์ของเรา
เพราะฉะนั้นการเขียนโปรแกรมคอมพิวเตอร์ เราควรจะแยกเป็นฟังก์ชันหลายๆฟังก์ชันครับ เพื่อที่ว่าตัวแปรที่เราไม่ได้ใช้งานจะทำการคืนหน่วยความจำให้กับเครื่องคอมพิวเตอร์ไปครับ ผมขอแนะนำว่าไม่ควรยัดทุกสิ่งทุกอย่างลงในฟังก์ชัน main ทั้งหมดครับ ไม่เช่นนั้นคอมพิวเตอร์ของเราจะทำงานหนักมากครับ โดยเฉพาะโปรแกรมที่มีการเรียกใช้ตัวแปรเยอะๆ
ในภาษา C++ มีคำสั่งที่ให้ผู้ใช้จองหน่วยความจำเองที่ชื่อว่า new รูปแบบคำสั่งจะเป็น
ชนิดข้อมูล pointer = new ชนิดข้อมูล;
เช่นเราสั่งว่า
int* p=new int;
เครื่องคอมพิวเตอร์จองหน่วยความจำขนาด 4 ไบต์(new int) เพื่อเก็บข้อมูลชนิด int แล้วให้ pointer p ชี้มายังตำแหน่งที่เก็บข้อมูลนี้
การจองเนื้อที่หน่วยความจำโดยใช้คำสั่ง new นี้ คอมพิวเตอร์จะจองเนื้อที่นั้นๆไว้ตลอดแม้ว่าเราจะออกจากฟังก์ชันนั้นๆแล้วก็ตาม การคืนเนื้อที่หน่วยความจำนี้เราจะต้องทำการคืนเองโดยใช้คำสั่ง delete รูปแบบของคำสั่งของ delele จะเป็นแบบนี้ครับ
delete pointer;
เช่นในตัวอย่างข้างต้นถ้าผมต้องการคืนหน่วยความจำที่มี pointer p ชี้อยู่ ผมจะสั่งว่า
delete p;
ในโปรแกรมขนาดใหญ่ซึ่งใช้ตัวแปรค่อนข้างมาก เพื่อให้การทำงานของโปรแกรมมีประสิทธิภาพจะใช้การจองหน่วยความจำ และการปล่อยหน่วยความจำเองครับ แต่เนื่องด้วยความซับซ้อนของโปรแกรม ก็มีโอกาสเหมือนกันครับที่โปรแกรมเมอร์จะคืนคืนหน่วยความจำให้กับเครื่องไม่หมด ปัญหานี้เรียกว่า memory leak เมื่อตัวแปรที่เราไม่ได้คืนหน่วยความจำสะสมมากขึ้นๆ เครื่องของเราเครื่องเราทำงานหนักขึ้นๆ เรื่อยๆ จนถึงระดับหนึ่งจะเกิดการ crash ของโปรแกรมครับ
แต่ปัญหา memory leak โดยทั่วๆไปก็ไม่ได้น่ากลัวซะทีเดียวนะครับ เพราะถึงแม้ว่าเราจะคืนหน่วยความจำให้กับเครื่องไม่หมด แต่ถ้าเราออกจากโปรแกรมเมื่อไหร่ระบบปฏิบัติการจะทำการบีบบังคับให้โปรแกรมคืนทรัพยากรทั้งหมดให้กับเครื่องของเราเองครับ
ในภาษา C++ เราสามารถจองเนื้อที่หน่วยความจำแบบเป็น array ด้วยนะครับ เช่น สมมติว่าเราต้องการจองหน่วยความจำเป็นเป็น array ของ int ความยาว 4 เราจะสั่งเป็น
int* p=new int[4];
ส่วนคำสั่งคืนหน่วยความจำที่จองมาเราจะใช้เป็น
delele []p;
อย่าลืม [] หน้า p นะครับ ไม่เช่นนั้นมันจะทำการคืนหน่วยความจำ p[0] แค่ตัวเดียวเท่านั้น!!!
การจองหน่วยความจำและปล่อยหน่วยความจำที่จองมาด้วยตนเองมีชื่อทางเทคนิคว่า dynamic memory allocation คำว่า dynamic ในที่นี้จะเป็นหมายความว่า “มีความยืดหยุ่น” คือจะคืนหน่วยความจำให้เครื่องตอนไหนก็ได้ (คำนี้เป็นคำข้ามกับคำว่า static ครับ)

*******************************************************************************************************
An introduction to using dynamic memory in C++. Concepts: Why we may need to allocate memory dynamically? new operator heap (free store) pointers to reference dynamically allocated memory delete operator garbage dangling pointers


**********************************************************************************************************