🧠 Smart Pointers trong C++
📖 Giới thiệu
Smart Pointers là một trong những tính năng quan trọng nhất của Modern C++ (C++11 trở lên), giúp quản lý bộ nhớ tự động và an toàn. Chúng thay thế các raw pointers truyền thống, giải quyết các vấn đề về memory leaks, dangling pointers và double deletion.
Vấn đề với Raw Pointers:
- Memory leaks: Quên gọi
delete - Dangling pointers: Pointer trỏ đến vùng nhớ đã được giải phóng
- Double deletion: Gọi
deletenhiều lần - Exception safety: Memory không được giải phóng khi có exception
Smart Pointers giải quyết:
- Tự động quản lý: Tự động gọi
deletekhi cần - RAII principle: Resource Acquisition Is Initialization
- Exception safety: Đảm bảo cleanup ngay cả khi có exception
- Clear ownership: Rõ ràng về quyền sở hữu object
Ba loại Smart Pointers chính:
unique_ptr- Ownership độc quyềnshared_ptr- Ownership chia sẻ với reference countingweak_ptr- Quan sát object mà không sở hữu
🔧 Cú pháp
unique_ptr - Ownership độc quyền
#include <memory>
// Tạo unique_ptr
unique_ptr<int> ptr1 = make_unique<int>(42);
unique_ptr<int> ptr2(new int(42)); // Cách cũ, không khuyến khích
// Truy cập giá trị
cout << *ptr1 << endl; // 42
cout << ptr1.get() << endl; // Địa chỉ raw pointer
// Kiểm tra null
if (ptr1) {
cout << "Not null" << endl;
}
// Release ownership
int* raw = ptr1.release(); // ptr1 = nullptr, trả về raw pointer
delete raw; // Phải tự delete
// Reset
ptr1.reset(); // Delete object và set nullptr
ptr1.reset(new int(100)); // Delete cũ, quản lý object mới
// Move semantics (không thể copy)
unique_ptr<int> ptr3 = move(ptr1); // ptr1 bây giờ là nullptrshared_ptr - Ownership chia sẻ
// Tạo shared_ptr
shared_ptr<int> ptr1 = make_shared<int>(42);
shared_ptr<int> ptr2(new int(42)); // Cách cũ
// Copy và assignment
shared_ptr<int> ptr3 = ptr1; // Copy constructor
shared_ptr<int> ptr4;
ptr4 = ptr1; // Assignment
// Reference counting
cout << ptr1.use_count() << endl; // Số lượng shared_ptr cùng quản lý
// Truy cập giá trị
cout << *ptr1 << endl;
cout << ptr1.get() << endl;
// Reset
ptr1.reset(); // Giảm reference count
ptr1.reset(new int(100)); // Reset với object mới
// Custom deleter
shared_ptr<int> ptr5(new int(42), [](int* p) {
cout << "Custom deleting " << *p << endl;
delete p;
});weak_ptr - Quan sát không sở hữu
shared_ptr<int> shared = make_shared<int>(42);
weak_ptr<int> weak = shared;
// Kiểm tra object còn tồn tại
if (!weak.expired()) {
// Lock để truy cập an toàn
if (shared_ptr<int> locked = weak.lock()) {
cout << *locked << endl;
}
}
// Reset shared_ptr
shared.reset(); // Object bị delete
// weak_ptr giờ đây expired
if (weak.expired()) {
cout << "Object đã bị delete" << endl;
}Smart Pointers với Arrays
// unique_ptr với array
unique_ptr<int[]> arr1 = make_unique<int[]>(5);
arr1[0] = 10; // Truy cập như array
// shared_ptr với array (C++17)
shared_ptr<int[]> arr2(new int[5]);
// Hoặc sử dụng custom deleter (C++11)
shared_ptr<int> arr3(new int[5], [](int* p) { delete[] p; });🔬 Phân tích & Giải thích chi tiết
Khi nào sử dụng từng loại?
unique_ptr - Sử dụng khi:
- Ownership rõ ràng và độc quyền
- Muốn performance tốt nhất (không có overhead)
- Thay thế trực tiếp raw pointer với automatic cleanup
- Factory pattern trả về object
shared_ptr - Sử dụng khi:
- Nhiều object cùng cần truy cập
- Không rõ object nào sẽ die trước
- Callback systems, event handlers
- Cache systems
weak_ptr - Sử dụng khi:
- Phá vỡ circular references
- Observer pattern
- Cache implementations
- Parent-child relationships
RAII và Exception Safety
RAII (Resource Acquisition Is Initialization):
void riskyFunction() {
unique_ptr<MyClass> obj = make_unique<MyClass>();
// Ngay cả khi có exception ở đây...
doSomethingRisky();
// obj sẽ tự động được delete khi ra khỏi scope
}Performance Considerations
unique_ptr:
- Gần như không có overhead so với raw pointer
- Move operations rất nhanh
- Recommended choice khi có thể
shared_ptr:
- Có overhead của reference counting
- Thread-safe counting (atomic operations)
- Nên dùng
make_sharedđể tối ưu
Comparison:
// Performance test pseudo-code
unique_ptr<int> up = make_unique<int>(42); // Fast
shared_ptr<int> sp = make_shared<int>(42); // Slower but acceptable
// Move operations
auto up2 = move(up); // Very fast
auto sp2 = sp; // Atomic increment, slower💻 Ví dụ minh họa
Ví dụ 1: Hệ thống quản lý tài nguyên Game
#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <map>
using namespace std;
// Base Resource class
class Resource {
protected:
string name;
size_t size;
public:
Resource(const string& n, size_t s) : name(n), size(s) {
cout << "🔧 Loading resource: " << name << " (" << size << " bytes)" << endl;
}
virtual ~Resource() {
cout << "🗑️ Unloading resource: " << name << endl;
}
const string& getName() const { return name; }
size_t getSize() const { return size; }
virtual void use() = 0;
};
// Texture resource
class Texture : public Resource {
private:
int width, height;
public:
Texture(const string& name, int w, int h)
: Resource(name, w * h * 4), width(w), height(h) {}
void use() override {
cout << "🖼️ Rendering texture: " << name << " (" << width << "x" << height << ")" << endl;
}
int getWidth() const { return width; }
int getHeight() const { return height; }
};
// Sound resource
class Sound : public Resource {
private:
double duration;
public:
Sound(const string& name, double dur)
: Resource(name, static_cast<size_t>(dur * 44100 * 2)), duration(dur) {}
void use() override {
cout << "🔊 Playing sound: " << name << " (" << duration << "s)" << endl;
}
double getDuration() const { return duration; }
};
// Resource Manager sử dụng Smart Pointers
class ResourceManager {
private:
map<string, shared_ptr<Resource>> resources;
size_t totalMemory;
const size_t MAX_MEMORY = 100 * 1024 * 1024; // 100MB
public:
ResourceManager() : totalMemory(0) {
cout << "📦 ResourceManager initialized (Max: " << MAX_MEMORY << " bytes)" << endl;
}
~ResourceManager() {
cout << "📦 ResourceManager shutting down..." << endl;
resources.clear(); // Tự động cleanup tất cả resources
}
// Load texture - trả về shared_ptr
shared_ptr<Texture> loadTexture(const string& name, int width, int height) {
if (resources.find(name) != resources.end()) {
// Resource đã tồn tại, return existing
return dynamic_pointer_cast<Texture>(resources[name]);
}
size_t requiredMemory = width * height * 4;
if (totalMemory + requiredMemory > MAX_MEMORY) {
cout << "❌ Not enough memory for texture: " << name << endl;
return nullptr;
}
auto texture = make_shared<Texture>(name, width, height);
resources[name] = texture;
totalMemory += texture->getSize();
cout << "✅ Texture loaded. Memory usage: " << totalMemory << " bytes" << endl;
return texture;
}
// Load sound - trả về shared_ptr
shared_ptr<Sound> loadSound(const string& name, double duration) {
if (resources.find(name) != resources.end()) {
return dynamic_pointer_cast<Sound>(resources[name]);
}
size_t requiredMemory = static_cast<size_t>(duration * 44100 * 2);
if (totalMemory + requiredMemory > MAX_MEMORY) {
cout << "❌ Not enough memory for sound: " << name << endl;
return nullptr;
}
auto sound = make_shared<Sound>(name, duration);
resources[name] = sound;
totalMemory += sound->getSize();
cout << "✅ Sound loaded. Memory usage: " << totalMemory << " bytes" << endl;
return sound;
}
// Unload resource
void unloadResource(const string& name) {
auto it = resources.find(name);
if (it != resources.end()) {
totalMemory -= it->second->getSize();
resources.erase(it);
cout << "♻️ Resource unloaded: " << name << endl;
}
}
// Get resource statistics
void printStats() {
cout << "\n📊 RESOURCE STATISTICS:" << endl;
cout << "Active resources: " << resources.size() << endl;
cout << "Memory usage: " << totalMemory << " / " << MAX_MEMORY << " bytes" << endl;
cout << "Memory free: " << (MAX_MEMORY - totalMemory) << " bytes" << endl;
for (const auto& pair : resources) {
cout << "- " << pair.first << " (refs: " << pair.second.use_count() << ")" << endl;
}
}
};
// Game Object sử dụng resources
class GameObject {
private:
string name;
shared_ptr<Texture> texture;
shared_ptr<Sound> sound;
public:
GameObject(const string& n) : name(n) {}
void setTexture(shared_ptr<Texture> tex) {
texture = tex;
}
void setSound(shared_ptr<Sound> snd) {
sound = snd;
}
void render() {
cout << "🎮 Rendering GameObject: " << name << endl;
if (texture) {
texture->use();
}
}
void playSound() {
cout << "🎮 GameObject: " << name << " playing sound" << endl;
if (sound) {
sound->use();
}
}
~GameObject() {
cout << "🎮 GameObject destroyed: " << name << endl;
}
};
int main() {
cout << "=== GAME RESOURCE MANAGEMENT DEMO ===" << endl;
// Tạo Resource Manager
auto resourceManager = make_unique<ResourceManager>();
// Load resources
auto playerTexture = resourceManager->loadTexture("player.png", 64, 64);
auto enemyTexture = resourceManager->loadTexture("enemy.png", 32, 32);
auto bgTexture = resourceManager->loadTexture("background.png", 1024, 768);
auto jumpSound = resourceManager->loadSound("jump.wav", 0.5);
auto bgMusic = resourceManager->loadSound("background.mp3", 120.0);
resourceManager->printStats();
// Tạo game objects
cout << "\n=== CREATING GAME OBJECTS ===" << endl;
{
vector<unique_ptr<GameObject>> gameObjects;
// Tạo player
auto player = make_unique<GameObject>("Player");
player->setTexture(playerTexture);
player->setSound(jumpSound);
gameObjects.push_back(move(player));
// Tạo enemies
for (int i = 0; i < 3; i++) {
auto enemy = make_unique<GameObject>("Enemy_" + to_string(i));
enemy->setTexture(enemyTexture); // Cùng share texture
gameObjects.push_back(move(enemy));
}
// Render all objects
cout << "\n=== RENDERING FRAME ===" << endl;
for (auto& obj : gameObjects) {
obj->render();
}
// Play some sounds
cout << "\n=== PLAYING SOUNDS ===" << endl;
gameObjects[0]->playSound(); // Player jump
resourceManager->printStats();
cout << "\n=== GAME OBJECTS GOING OUT OF SCOPE ===" << endl;
} // Game objects destroyed here
// Check reference counts after game objects are destroyed
cout << "\n=== AFTER GAME OBJECTS DESTROYED ===" << endl;
resourceManager->printStats();
// Manually unload some resources
cout << "\n=== MANUAL CLEANUP ===" << endl;
resourceManager->unloadResource("background.png");
resourceManager->printStats();
cout << "\n=== PROGRAM ENDING ===" << endl;
// ResourceManager will be automatically destroyed
return 0;
}Ví dụ 2: Observer Pattern với weak_ptr
#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// Forward declaration
class Subject;
// Observer interface
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const string& message) = 0;
virtual string getName() const = 0;
};
// Subject class
class Subject {
private:
vector<weak_ptr<Observer>> observers;
string state;
public:
// Add observer
void attach(shared_ptr<Observer> observer) {
observers.push_back(observer);
cout << "📎 Observer attached: " << observer->getName() << endl;
}
// Remove observer
void detach(shared_ptr<Observer> observer) {
observers.erase(
remove_if(observers.begin(), observers.end(),
[&observer](const weak_ptr<Observer>& weak_obs) {
return weak_obs.lock() == observer;
}),
observers.end()
);
cout << "📎 Observer detached: " << observer->getName() << endl;
}
// Notify all observers
void notify(const string& message) {
cout << "\n📢 Subject notifying observers: " << message << endl;
// Clean up expired observers while notifying
auto it = observers.begin();
while (it != observers.end()) {
if (auto observer = it->lock()) {
observer->update(message);
++it;
} else {
// Observer đã bị destroy, remove weak_ptr
cout << "🧹 Cleaning up expired observer" << endl;
it = observers.erase(it);
}
}
}
void setState(const string& newState) {
state = newState;
notify("State changed to: " + state);
}
string getState() const { return state; }
int getObserverCount() const {
// Count valid observers
int count = 0;
for (const auto& weak_obs : observers) {
if (!weak_obs.expired()) count++;
}
return count;
}
};
// Concrete Observer
class ConcreteObserver : public Observer {
private:
string name;
weak_ptr<Subject> subject; // weak_ptr để tránh circular reference
public:
ConcreteObserver(const string& n, shared_ptr<Subject> subj)
: name(n), subject(subj) {
cout << "👁️ Observer created: " << name << endl;
}
~ConcreteObserver() {
cout << "👁️ Observer destroyed: " << name << endl;
}
void update(const string& message) override {
cout << "👁️ [" << name << "] received: " << message << endl;
// Safely access subject
if (auto subj = subject.lock()) {
cout << " Current subject state: " << subj->getState() << endl;
} else {
cout << " Subject no longer exists!" << endl;
}
}
string getName() const override {
return name;
}
};
// News Agency example
class NewsAgency {
private:
shared_ptr<Subject> newsSubject;
public:
NewsAgency() {
newsSubject = make_shared<Subject>();
cout << "📰 News Agency created" << endl;
}
~NewsAgency() {
cout << "📰 News Agency shutting down" << endl;
}
void publishNews(const string& news) {
newsSubject->setState(news);
}
shared_ptr<Subject> getNewsSubject() {
return newsSubject;
}
void showStats() {
cout << "📊 Active subscribers: " << newsSubject->getObserverCount() << endl;
}
};
int main() {
cout << "=== OBSERVER PATTERN WITH WEAK_PTR DEMO ===" << endl;
// Tạo News Agency
auto agency = make_unique<NewsAgency>();
auto newsSubject = agency->getNewsSubject();
cout << "\n=== CREATING SUBSCRIBERS ===" << endl;
// Tạo observers trong scope riêng
{
auto observer1 = make_shared<ConcreteObserver>("CNN", newsSubject);
auto observer2 = make_shared<ConcreteObserver>("BBC", newsSubject);
auto observer3 = make_shared<ConcreteObserver>("Reuters", newsSubject);
// Attach observers
newsSubject->attach(observer1);
newsSubject->attach(observer2);
newsSubject->attach(observer3);
agency->showStats();
// Publish some news
cout << "\n=== PUBLISHING NEWS ===" << endl;
agency->publishNews("Breaking: New Technology Announced!");
agency->publishNews("Weather: Sunny day expected tomorrow");
// Detach one observer
cout << "\n=== DETACHING OBSERVER ===" << endl;
newsSubject->detach(observer2);
agency->showStats();
agency->publishNews("Sports: Local team wins championship!");
cout << "\n=== OBSERVERS GOING OUT OF SCOPE ===" << endl;
} // observers destroyed here automatically
// Try to publish news after observers are destroyed
cout << "\n=== PUBLISHING AFTER OBSERVERS DESTROYED ===" << endl;
agency->publishNews("This news has no subscribers");
agency->showStats();
// Create new observer to show the system still works
cout << "\n=== CREATING NEW OBSERVER ===" << endl;
{
auto newObserver = make_shared<ConcreteObserver>("Associated Press", newsSubject);
newsSubject->attach(newObserver);
agency->publishNews("Final news of the day");
cout << "\n=== FINAL CLEANUP ===" << endl;
}
return 0;
}Ví dụ 3: Factory Pattern với Smart Pointers
#include <iostream>
#include <memory>
#include <string>
#include <map>
#include <functional>
using namespace std;
// Abstract Product
class Vehicle {
protected:
string brand;
double price;
public:
Vehicle(const string& b, double p) : brand(b), price(p) {}
virtual ~Vehicle() = default;
virtual void start() = 0;
virtual void stop() = 0;
virtual string getType() const = 0;
string getBrand() const { return brand; }
double getPrice() const { return price; }
};
// Concrete Products
class Car : public Vehicle {
private:
int doors;
public:
Car(const string& brand, double price, int d)
: Vehicle(brand, price), doors(d) {}
void start() override {
cout << "🚗 Car " << brand << " engine started!" << endl;
}
void stop() override {
cout << "🚗 Car " << brand << " engine stopped." << endl;
}
string getType() const override {
return "Car (" + to_string(doors) + " doors)";
}
};
class Motorcycle : public Vehicle {
private:
int engineCC;
public:
Motorcycle(const string& brand, double price, int cc)
: Vehicle(brand, price), engineCC(cc) {}
void start() override {
cout << "🏍️ Motorcycle " << brand << " roared to life!" << endl;
}
void stop() override {
cout << "🏍️ Motorcycle " << brand << " engine stopped." << endl;
}
string getType() const override {
return "Motorcycle (" + to_string(engineCC) + "cc)";
}
};
class Truck : public Vehicle {
private:
double capacity;
public:
Truck(const string& brand, double price, double cap)
: Vehicle(brand, price), capacity(cap) {}
void start() override {
cout << "🚛 Truck " << brand << " diesel engine started!" << endl;
}
void stop() override {
cout << "🚛 Truck " << brand << " diesel engine stopped." << endl;
}
string getType() const override {
return "Truck (" + to_string(capacity) + " tons)";
}
};
// Factory class using Smart Pointers
class VehicleFactory {
private:
// Factory methods map
map<string, function<unique_ptr<Vehicle>(const string&, double)>> creators;
public:
VehicleFactory() {
// Register creators using lambda functions
creators["car"] = [](const string& brand, double price) {
return make_unique<Car>(brand, price, 4); // Default 4 doors
};
creators["sports_car"] = [](const string& brand, double price) {
return make_unique<Car>(brand, price, 2); // 2 doors
};
creators["motorcycle"] = [](const string& brand, double price) {
return make_unique<Motorcycle>(brand, price, 150); // Default 150cc
};
creators["sport_bike"] = [](const string& brand, double price) {
return make_unique<Motorcycle>(brand, price, 600); // 600cc
};
creators["truck"] = [](const string& brand, double price) {
return make_unique<Truck>(brand, price, 10.0); // 10 tons
};
}
// Factory method - returns unique_ptr
unique_ptr<Vehicle> createVehicle(const string& type, const string& brand, double price) {
auto it = creators.find(type);
if (it != creators.end()) {
cout << "🏭 Creating " << type << ": " << brand << endl;
return it->second(brand, price);
}
cout << "❌ Unknown vehicle type: " << type << endl;
return nullptr;
}
// Get available types
vector<string> getAvailableTypes() const {
vector<string> types;
for (const auto& pair : creators) {
types.push_back(pair.first);
}
return types;
}
};
// Dealership class managing vehicles
class Dealership {
private:
string name;
vector<unique_ptr<Vehicle>> inventory;
VehicleFactory factory;
public:
Dealership(const string& n) : name(n) {
cout << "🏪 Dealership '" << name << "' opened!" << endl;
}
~Dealership() {
cout << "🏪 Dealership '" << name << "' closing..." << endl;
}
// Add vehicle to inventory
void addVehicle(const string& type, const string& brand, double price) {
auto vehicle = factory.createVehicle(type, brand, price);
if (vehicle) {
inventory.push_back(move(vehicle));
cout << "✅ Added to inventory: " << brand << " " << vehicle->getType() << endl;
}
}
// Sell vehicle (transfer ownership)
unique_ptr<Vehicle> sellVehicle(size_t index) {
if (index < inventory.size()) {
auto vehicle = move(inventory[index]);
inventory.erase(inventory.begin() + index);
cout << "💰 Sold: " << vehicle->getBrand() << " " << vehicle->getType() << endl;
return vehicle;
}
cout << "❌ Vehicle not found at index " << index << endl;
return nullptr;
}
// Display inventory
void showInventory() const {
cout << "\n🏪 " << name << " INVENTORY:" << endl;
cout << "(" << inventory.size() << " vehicles available)" << endl;
for (size_t i = 0; i < inventory.size(); i++) {
const auto& vehicle = inventory[i];
cout << i << ". " << vehicle->getBrand()
<< " " << vehicle->getType()
<< " - $" << vehicle->getPrice() << endl;
}
}
// Test drive (temporarily use vehicle)
void testDrive(size_t index) {
if (index < inventory.size()) {
const auto& vehicle = inventory[index];
cout << "\n🧪 TEST DRIVE: " << vehicle->getBrand() << " " << vehicle->getType() << endl;
vehicle->start();
cout << "... driving around the block ..." << endl;
vehicle->stop();
cout << "Test drive completed!" << endl;
}
}
void showAvailableTypes() const {
cout << "\nAvailable vehicle types:" << endl;
for (const string& type : factory.getAvailableTypes()) {
cout << "- " << type << endl;
}
}
};
int main() {
cout << "=== VEHICLE FACTORY WITH SMART POINTERS DEMO ===" << endl;
// Create dealership
auto dealership = make_unique<Dealership>("Premium Motors");
dealership->showAvailableTypes();
// Stock the dealership
cout << "\n=== STOCKING INVENTORY ===" << endl;
dealership->addVehicle("car", "Toyota Camry", 25000);
dealership->addVehicle("sports_car", "Porsche 911", 95000);
dealership->addVehicle("motorcycle", "Honda CBR", 8000);
dealership->addVehicle("sport_bike", "Yamaha R1", 15000);
dealership->addVehicle("truck", "Ford F-150", 35000);
dealership->showInventory();
// Test drive some vehicles
cout << "\n=== TEST DRIVES ===" << endl;
dealership->testDrive(1); // Sports car
dealership->testDrive(2); // Motorcycle
// Customer purchases
cout << "\n=== CUSTOMER PURCHASES ===" << endl;
// Customer 1 buys sports car
{
cout << "\n👤 Customer 1 purchasing sports car..." << endl;
auto purchasedVehicle = dealership->sellVehicle(1);
if (purchasedVehicle) {
cout << "🎉 Customer 1 owns: " << purchasedVehicle->getBrand()
<< " " << purchasedVehicle->getType() << endl;
// Customer uses their vehicle
purchasedVehicle->start();
cout << "Customer 1 driving home..." << endl;
purchasedVehicle->stop();
}
cout << "👤 Customer 1 leaving (vehicle destroyed automatically)" << endl;
} // Customer 1's vehicle automatically destroyed here
dealership->showInventory();
// Customer 2 buys motorcycle
{
cout << "\n👤 Customer 2 purchasing motorcycle..." << endl;
auto bike = dealership->sellVehicle(1); // Index shifted after previous sale
if (bike) {
cout << "🎉 Customer 2 owns: " << bike->getBrand()
<< " " << bike->getType() << endl;
}
} // Motorcycle automatically destroyed
cout << "\n=== FINAL INVENTORY ===" << endl;
dealership->showInventory();
cout << "\n=== DEALERSHIP CLOSING ===" << endl;
// Dealership and remaining inventory automatically cleaned up
return 0;
}🏋️ Thực hành
Bài tập 1: Custom Smart Pointer
Tự implement một simple smart pointer với các tính năng cơ bản:
- Reference counting
- Automatic deletion
- Thread-safe counting (bonus)
Bài tập 2: Memory Pool với Smart Pointers
Tạo memory pool sử dụng smart pointers để quản lý objects:
- Pre-allocate objects
- Reuse objects thay vì delete/new
- Statistics tracking
Bài tập 3: Graph Structure với Circular References
Implement graph data structure tránh circular references:
- Node với neighbors
- Sử dụng weak_ptr cho back-references
- Traversal algorithms
Bài tập 4: Cache System
Tạo LRU cache sử dụng smart pointers:
- shared_ptr cho cached objects
- weak_ptr để track without ownership
- Automatic cleanup of unused items
Lời giải chi tiết
Bài tập 1 - Custom Smart Pointer:
#include <iostream>
#include <atomic>
using namespace std;
template<typename T>
class SimpleSharedPtr {
private:
T* ptr;
atomic<int>* refCount;
public:
// Constructor
explicit SimpleSharedPtr(T* p = nullptr) : ptr(p) {
if (ptr) {
refCount = new atomic<int>(1);
} else {
refCount = nullptr;
}
}
// Copy constructor
SimpleSharedPtr(const SimpleSharedPtr& other) : ptr(other.ptr), refCount(other.refCount) {
if (refCount) {
(*refCount)++;
}
}
// Assignment operator
SimpleSharedPtr& operator=(const SimpleSharedPtr& other) {
if (this != &other) {
// Release current resource
release();
// Copy from other
ptr = other.ptr;
refCount = other.refCount;
if (refCount) {
(*refCount)++;
}
}
return *this;
}
// Destructor
~SimpleSharedPtr() {
release();
}
// Dereference operators
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
// Get raw pointer
T* get() const { return ptr; }
// Check if valid
operator bool() const { return ptr != nullptr; }
// Get reference count
int useCount() const {
return refCount ? refCount->load() : 0;
}
private:
void release() {
if (refCount) {
if (--(*refCount) == 0) {
delete ptr;
delete refCount;
}
}
}
};
// Test the custom smart pointer
class TestClass {
public:
TestClass(int val) : value(val) {
cout << "TestClass(" << value << ") created" << endl;
}
~TestClass() {
cout << "TestClass(" << value << ") destroyed" << endl;
}
void show() const {
cout << "Value: " << value << endl;
}
private:
int value;
};
void testCustomSmartPtr() {
cout << "=== Testing Custom Smart Pointer ===" << endl;
SimpleSharedPtr<TestClass> ptr1(new TestClass(42));
cout << "ptr1 ref count: " << ptr1.useCount() << endl;
{
SimpleSharedPtr<TestClass> ptr2 = ptr1;
cout << "After copy - ptr1 ref count: " << ptr1.useCount() << endl;
cout << "ptr2 ref count: " << ptr2.useCount() << endl;
ptr2->show();
}
cout << "After ptr2 destroyed - ptr1 ref count: " << ptr1.useCount() << endl;
// ptr1 will be destroyed when going out of scope
}📋 Tóm tắt
Các điểm quan trọng
Ba loại Smart Pointers:
unique_ptr- Ownership độc quyền, không thể copyshared_ptr- Ownership chia sẻ với reference countingweak_ptr- Quan sát object mà không sở hữu
Khi nào sử dụng:
- unique_ptr: Default choice, thay thế raw pointers
- shared_ptr: Khi cần chia sẻ ownership
- weak_ptr: Phá vỡ circular references, observer pattern
Best Practices:
- Prefer
make_uniquevàmake_shared - Sử dụng
unique_ptrtrước, chỉ dùngshared_ptrkhi cần weak_ptrđể tránh circular references- RAII principle cho exception safety
Performance Notes
unique_ptr:
- Zero overhead so với raw pointer
- Move semantics rất nhanh
- Perfect for RAII
shared_ptr:
- Overhead của reference counting
- Thread-safe atomic operations
make_sharedhiệu quả hơnshared_ptr(new T)
Chuẩn bị cho bài tiếp theo
Bài học tiếp theo sẽ tìm hiểu về Multithreading - lập trình đa luồng:
- std::thread: Tạo và quản lý threads
- Synchronization: mutex, lock_guard, condition_variable
- Thread safety: Shared data và race conditions
- Parallel algorithms: std::async, std::future
- Performance: Khi nào sử dụng multithreading
Smart Pointers là nền tảng của Modern C++ memory management. Hãy luyện tập để viết code an toàn và hiệu quả!