實作一個 Mem 類別,封裝動態記憶體管理:
#ifndef MEM_H
#define MEM_H
#include // memcpy, memset
typedef unsigned char byte;
class Mem {
byte* mem_;
int size_;
void ensureMinSize(int minSize) {
if (size_ < minSize) {
byte* newMem = new byte[minSize];
std::memset(newMem + size_, 0, minSize - size_); // 新增區域歸零
std::memcpy(newMem, mem_, size_); // 複製舊資料
delete[] mem_;
mem_ = newMem;
size_ = minSize;
}
}
public:
Mem() : mem_(nullptr), size_(0) {}
explicit Mem(int sz) : mem_(nullptr), size_(0) { ensureMinSize(sz); }
~Mem() { delete[] mem_; }
// 禁止複製 (淺拷貝風險),C++11 刪除
Mem(const Mem&) = delete;
Mem& operator=(const Mem&) = delete;
// 移動語意 (C++11)
Mem(Mem&& other) noexcept : mem_(other.mem_), size_(other.size_) {
other.mem_ = nullptr; other.size_ = 0;
}
Mem& operator=(Mem&& other) noexcept {
if (this != &other) {
delete[] mem_;
mem_ = other.mem_; size_ = other.size_;
other.mem_ = nullptr; other.size_ = 0;
}
return *this;
}
int msize() const { return size_; }
// 重載:無參數回傳當前指標,有參數確保最小大小並回傳
byte* pointer() { return mem_; }
byte* pointer(int minSize) { ensureMinSize(minSize); return mem_; }
};
#endif
MyString
#include "Mem.h"
#include
#include
class MyString {
Mem* buf_;
public:
MyString() : buf_(nullptr) {}
explicit MyString(const char* str) {
buf_ = new Mem(strlen(str) + 1);
std::strcpy(reinterpret_cast(buf_->pointer()), str);
}
~MyString() { delete buf_; }
void concat(const char* str) {
if (!buf_) buf_ = new Mem();
int oldLen = buf_->msize() ? strlen(reinterpret_cast(buf_->pointer())) : 0;
int addLen = strlen(str);
// 關鍵:pointer(新大小) 觸發自動擴容
std::strcat(reinterpret_cast(buf_->pointer(buf_->msize() + addLen + 1)), str);
}
void print(std::ostream& os) const {
if (buf_) os << reinterpret_cast(buf_->pointer()) << std::endl;
}
};
// 錯誤:呼叫端無法明確表達意圖,內部邏輯混雜
void Mystring::Mystring(char *str) {
if (!*str) { buf_ = 0; return; } // 空字串特判混在建構函式
buf_ = new Mem(strlen(str)+1);
strcpy((char*)buf_->pointer(), str);
}
// 正確:兩個建構函式,語意清晰
MyString() : buf_(nullptr) {} // 空字串
explicit MyString(const char* str) { // 非空字串
buf_ = new Mem(strlen(str)+1);
strcpy(reinterpret_cast(buf_->pointer()), str);
}
原則:不要用預設參數作為「分支旗標」決定執行邏輯。應拆分為多個重載函式,各司其職,編譯器靜態綁定,零運行開銷、意圖明確。
int main() {
MyString s("Hello");
s.print(std::cout); // Hello
s.concat(", World!");
s.print(std::cout); // Hello, World!
return 0;
}
| 改進點 | 方案 |
|---|---|
| 手動 new/delete | 改用 std::unique_ptr 或 std::vector |
| 字串操作 | 直接用 std::string (SSO 小字串優化、完美轉移) |
| 緩衝區增長策略 | 指數增長 (×1.5 或 ×2) 減少重配次數,而非精確增長 |
| 異常安全 | new 失敗拋 bad_alloc,RAII 保證不洩漏 |
教學價值:此範例展示 RAII (資源獲取即初始化) 核心模式:建構函式獲取資源、解構函式釋放資源、拷貝/移動語意正確處理所有權。這是現代 C++ 資源管理的基石,理解它才能真正掌握智慧指標、容器、鎖等高階設施的內在邏輯。