C++11 智能指针
在 C++ 中,智能指针(Smart Pointers)是用来自动管理动态分配内存的工具,可以减少内存泄漏的风险。C++标准库提供了几种常见的智能指针,包括 std::unique_ptr、std::shared_ptr 和 std::weak_ptr,它们各自有不同的特性和用途。下面我们将详细对比解释这些智能指针。
std::unique_ptr
std::unique_ptr 是一种独占性的智能指针,意味着同一时刻只有一个 unique_ptr 可以拥有某个对象的所有权。它遵循 RAII(Resource Acquisition Is Initialization)原则,当 unique_ptr 被销毁时,它所管理的对象会被自动释放。
其特点:
- 独占所有权:同一个对象只能被一个 
unique_ptr拥有。 - 不能拷贝:
unique_ptr不能被复制,因为它拥有唯一的所有权。尝试拷贝会导致编译错误。 - 支持移动语义:可以通过移动构造函数或者移动赋值操作符将所有权从一个 
unique_ptr移动到另一个。(?) - 自动释放内存:当 
unique_ptr被销毁时,它会自动释放所管理的对象。- 注意结合 std:: make_unique 和 std:: move 使用!
 
 
#include <iostream>
#include <memory>
class MyClass {
public:
    MyClass(int val) : value(val) {
        std::cout << "MyClass constructed with value: " << value << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass destructed with value: " << value << std::endl;
    }
    void showValue() const {
        std::cout << "Value: " << value << std::endl;
    }
private:
    int value;
};
int main() {
    std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>(10);  // ptr1 拥有 MyClass 的所有权
    std::cout << "Moving ownership to ptr2..." << std::endl;
    std::unique_ptr<MyClass> ptr2 = std::move(ptr1);  // 转移所有权
    
    // ptr1 现在为空,不能再访问对象
    if (ptr1) {
        ptr1->showValue();
    } else {
        std::cout << "ptr1 is now null." << std::endl;
    }
    // ptr2 现在拥有 MyClass 对象的所有权
    ptr2->showValue();
    return 0;
}
std::shared_ptr
std::shared_ptr 是一种共享所有权的智能指针,多个 shared_ptr 可以共享同一个对象的所有权。只有当最后一个 shared_ptr 被销毁时,所管理的对象才会被释放。
特点:
- 共享所有权:多个 
shared_ptr可以指向同一个对象,每个shared_ptr都会增加一个引用计数。 - 引用计数:
shared_ptr会跟踪有多少个shared_ptr指向同一个对象,当引用计数归零时,对象会被自动销毁。 - 拷贝与赋值:
shared_ptr支持拷贝和赋值操作,每次拷贝都会增加引用计数。 - 性能开销:由于引用计数的管理,
shared_ptr会带来额外的性能开销(例如原子操作)。 
释放需要手动吗?
#include <iostream>
#include <memory>
class MyClass {
public:
    MyClass(int x) : x_(x) {
        std::cout << "MyClass(" << x_ << ") constructed\n";
    }
    ~MyClass() {
        std::cout << "MyClass(" << x_ << ") destructed\n";
    }
private:
    int x_;
};
int main() {
    std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>(10);
    {
        std::shared_ptr<MyClass> ptr2 = ptr1; // 引用计数增加
        std::cout << "Inside inner scope\n";
    } // ptr2 离开作用域,引用计数减少
    std::cout << "Outside inner scope\n";
    return 0;
}
说明:
ptr1和ptr2共享对同一个MyClass对象的所有权。- 当 
ptr2离开作用域时,它的引用计数减少,但对象不会立即销毁,因为ptr1仍然指向它。 - 当 
ptr1离开作用域时,引用计数归零,MyClass对象被销毁。
问题:shared_ptr 引起循环计数问题: 
#include <iostream>
#include <memory>
class A;
class B {
public:
    std::shared_ptr<A> a_ptr;
    B() {
        std::cout << "B constructed\n";
    }
    ~B() {
        std::cout << "B destructed\n";
    }
};
class A {
public:
    std::shared_ptr<B> b_ptr;
    A() {
        std::cout << "A constructed\n";
    }
    ~A() {
        std::cout << "A destructed\n";
    }
};
int main() {
    {
        std::shared_ptr<A> a = std::make_shared<A>();
        std::shared_ptr<B> b = std::make_shared<B>();
        
        a->b_ptr = b; // A -> B
        b->a_ptr = a; // B -> A
        // A 和 B 形成循环引用,内存无法释放
    } // 这里的 a 和 b 应该销毁,但因为循环引用,它们的引用计数永远不为 0
    std::cout << "End of scope\n";
    return 0;
}
std::weak_ptr
std::weak_ptr 是一种弱引用的智能指针,它与 shared_ptr 配合使用,用来避免循环引用。weak_ptr 不会改变引用计数,它只提供对 shared_ptr 所管理对象的访问。如果对象被销毁,weak_ptr 就不再有效。
特点:
- 不影响引用计数:
weak_ptr不会增加或减少引用计数,它只是一个“观察者”。 - 用于打破循环引用:
weak_ptr主要用于解决shared_ptr引起的循环引用问题。例如,当两个对象通过shared_ptr相互引用时,会导致内存无法释放。使用weak_ptr可以避免这种情况。 - 转换为 
shared_ptr:weak_ptr可以通过lock()方法转换为shared_ptr。如果对象已经被销毁,lock()返回一个空的shared_ptr。
使用 weak_ptr 打破循环引用: 
#include <iostream>
#include <memory>
class A;
class B {
public:
    std::weak_ptr<A> a_ptr;  // 使用 weak_ptr 避免循环引用
    B() {
        std::cout << "B constructed\n";
    }
    ~B() {
        std::cout << "B destructed\n";
    }
};
class A {
public:
    std::shared_ptr<B> b_ptr;
    A() {
        std::cout << "A constructed\n";
    }
    ~A() {
        std::cout << "A destructed\n";
    }
};
int main() {
    {
        std::shared_ptr<A> a = std::make_shared<A>();
        std::shared_ptr<B> b = std::make_shared<B>();
        
        a->b_ptr = b; // A -> B
        b->a_ptr = a; // B -> A,但这里是 weak_ptr,避免了循环引用
        // 由于没有循环引用,内存可以正确释放
    } // A 和 B 都会被销毁
    std::cout << "End of scope\n";
    return 0;
}
对比总结
| 特性 | std::unique_ptr | 
std::shared_ptr | 
std::weak_ptr | 
|---|---|---|---|
| 所有权管理 | 独占所有权 | 共享所有权 | 不拥有对象,辅助管理共享所有权 | 
| 拷贝行为 | 不支持拷贝,只能移动 | 支持拷贝,增加引用计数 | 不影响引用计数,只是弱引用 | 
| 引用计数 | 无引用计数 | 有引用计数,自动销毁对象 | 无引用计数,仅观察对象是否仍存在 | 
| 适用场景 | 需要独占所有权,简单的对象管理 | 需要共享所有权的复杂对象管理 | 解决 shared_ptr 引起的循环引用问题 | 
| 性能开销 | 最小,只有对象本身的管理 | 稍大,包含引用计数的管理开销 | 极小,仅提供观察者功能 | 
 评论

