Skip to content

Memory Management Lab

🎯 Mục tiêu

🎯 Sau bài thực hành này, bạn sẽ:

  • Áp dụng RAII pattern để quản lý tài nguyên tự động
  • Sử dụng đúng unique_ptr và shared_ptr
  • Phát hiện và sửa memory leaks
  • Viết custom deleter cho tài nguyên đặc biệt

Mô tả bài tập

Memory management là kỹ năng sống còn trong C++. Bài tập giúp bạn chuyển từ raw pointer sang smart pointer, áp dụng RAII tránh resource leaks.

Yêu cầu

Bài 1: RAII — File Handler

Viết class FileHandler đảm bảo file luôn được đóng khi ra khỏi scope.

cpp
class FileHandler {
    std::ofstream file;
public:
    explicit FileHandler(const std::string& path);  // mở file, throw nếu fail
    ~FileHandler();                                   // đóng file tự động
    void write(const std::string& content);
    FileHandler(const FileHandler&) = delete;         // không cho copy
    FileHandler& operator=(const FileHandler&) = delete;
};

Bài 2: Smart Pointers — Tree Structure

Xây dựng tree: root sở hữu unique_ptr, children dùng shared_ptr.

cpp
struct Node {
    std::string value;
    std::vector<std::shared_ptr<Node>> children;
    explicit Node(const std::string& v) : value(v) {}
    ~Node() { std::cout << "Destroyed: " << value << std::endl; }
};

std::unique_ptr<Node> buildTree() {
    // TODO: Tạo root với 2 children và 1 grandchild
}

Bài 3: Custom Deleter

Viết custom deleter cho C-style resources (malloc/free, FILE*/fclose).

cpp
struct MallocDeleter {
    void operator()(void* ptr) const { /* TODO */ }
};

using FilePtr = std::unique_ptr<FILE, decltype(&fclose)>;
FilePtr openFile(const char* path, const char* mode) {
    // TODO: return FilePtr wrapping fopen
}

Gợi ý

Gợi ý Bài 1

Constructor: file.open(path); if (!file.is_open()) throw .... RAII đảm bảo destructor luôn được gọi — kể cả khi exception.

Gợi ý Bài 2

auto root = std::make_unique<Node>("root"); root->children.push_back(std::make_shared<Node>("child"));

Lời giải tham khảo

Xem lời giải
cpp
// Bài 1
FileHandler::FileHandler(const std::string& path) {
    file.open(path);
    if (!file.is_open()) throw std::runtime_error("Cannot open: " + path);
}
FileHandler::~FileHandler() { if (file.is_open()) file.close(); }
void FileHandler::write(const std::string& content) { file << content; }

// Bài 2
std::unique_ptr<Node> buildTree() {
    auto root = std::make_unique<Node>("root");
    root->children.push_back(std::make_shared<Node>("child-A"));
    root->children.push_back(std::make_shared<Node>("child-B"));
    root->children[0]->children.push_back(std::make_shared<Node>("grandchild-1"));
    return root;
}

// Bài 3
struct MallocDeleter {
    void operator()(void* ptr) const { std::free(ptr); }
};
FilePtr openFile(const char* path, const char* mode) {
    return FilePtr(std::fopen(path, mode), fclose);
}