بخش ۱۳ - مدیریت حافظه در کلاس‌ها

در این بخش ساختمان داده‌ی پشته در قالب یک کلاس پیاده‌سازی می‌شود. در این پیاده‌سازی برای نگهداری عناصر پشته از آرایه استفاده می‌شود. همان‌گونه که خواهیم دید برای مدیریت درست حافظه در شرایط مختلف نیاز به تعریف مخرب و سازنده‌ی کپی و همچنین سربارگذاری عملگر جایگزینی داریم.

فهرست مثال‌ها

پشته با مخرب

کلاس پشته در حالی که پس‌دادن حافظه‌ی تخصیص‌یافته برای نگهداری عناصر توسط مخرب انجام می‌شود.

1_Destructor.cpp
#include <iostream>
using namespace std;

#define DEFAULT_SIZE 10

class invalid_operation_ex {};

class stack {
public:
    stack(int size);
    ~stack();
    void push(int x);
    void pop();
    int top() const;
    int elem_count() const { return count; }
private:
    int *elements;
    int size;
    int count;
};

stack::stack(int s = DEFAULT_SIZE) {
    cout << "--constructor called\n";
    size = s;
    elements = new int[size];
    count = 0;
}

stack::~stack() {
    cout << "--destructor called\n";
    delete[] elements;
}

void stack::push(int x) {
    if (count >= size)
        throw invalid_operation_ex();

    elements[count] = x;
    count++;
}

void stack::pop() {
    if (count > 0)
        count--;
    else
        throw invalid_operation_ex();
}

int stack::top() const {
    if (count > 0)
        return elements[count-1];
    else
        throw invalid_operation_ex();
}

int main() {
    stack s;
    s.push(4);
    cout << s.top() << endl;
}
				
مشکل کپی کم‌عمق

این مثال نشان‌دهنده‌ی مشکلی است که هنگام ساختن کپی از اشیایی پیش می‌آید که حافظه‌ی پویا به آنها تخصیص یافته.

2_ShallowCopyProblem.cpp


void print_stack(stack s) {
    while (s.elem_count() > 0) {
        cout << s.top() << ' ';
        s.pop();
    }
    cout << endl;
}

int main() {
    stack u;
    u.push(4);
    u.push(5);
    u.push(12);
    print_stack(u);
    u.pop();
    u.pop();
    u.pop();
    // Explain the result of execution of this code
}
				
سازنده‌ی کپی

برای رفع مشکل مثال قبل، باید برای این کلاس سازنده‌ی کپی تعریف کنیم تا هنگام کپی گرفتن از روی یک شیء به طور خودکار تخصیص حافظه را به عهده بگیرد.

3_CopyConstructor.cpp

class stack {
    stack(const stack&);
};

stack::stack(const stack& s) {
    cout << "-- copy constructor called\n";
    size = s.size;
    count = s.count;
    elements = new int[size];
    for (int i = 0; i < count; i++)
        elements[i] = s.elements[i];
}

void print_stack(stack s) {
    while (s.elem_count() > 0) {
        cout << s.top() << ' ';
        s.pop();
    }
    cout << endl;
}

int main() {
    stack u;
    u.push(4);
    u.push(5);
    u.push(12);
    print_stack(u);
    u.pop();
    u.pop();
    u.pop();
    // Explain the result of execution of this code
}
				
مشکل با عملگر جایگزینی

این مثال نشان می‌دهد برای استفاده‌ی بی‌اشکال از کلاسی که تخصیص حافظه‌ی پویا دارد لازم است به عملگر جایگزینی هم توجه شود.

4_CopyVsAssignment.cpp


int main() {
    stack u;
    u.push(4);
    u.push(5);
    u.push(12);

    stack v = u;

    stack w;
    w = u;

    // Explain the result of execution of this code
}
				
سربارگذاری عملگر جایگزینی

برای رفع مشکل مثال قبل، باید عملگر جایگزینی را سربارگذاری کنیم.

5_AssignmentOverload.cpp


class stack {
    stack& operator=(const stack&);
};

stack& stack::operator=(const stack& s)
{
    if (this == &s)
        return *this;
        
    count = s.count;
    size = s.size;
    delete[] elements;
    elements = new int[size];
    for (int i = 0; i < count; i++)
        elements[i] = s.elements[i];
        
    return *this;
}

int main() {
    stack u;
    u.push(4);
    u.push(5);
    u.push(12);

    stack w;
    w = u;
}