c++智能指针

C++ 中的智能指针是一种封装了动态分配的内存资源的对象,它们通过在其生命周期结束时自动释放内存来管理资源,从而帮助避免内存泄漏和野指针问题。C++ 标准库提供了四种不同类型的智能指针:std::unique_ptrstd::shared_ptrstd::weak_ptrstd::auto_ptr(C++11 中已被废弃)。这些智能指针之间有不同的所有权和管理资源的方式。

1. std::auto_ptr(已被废弃):

std::auto_ptr 是 C++98 标准中引入的智能指针,它提供了独占所有权的功能,但不如 std::unique_ptr 安全。std::auto_ptr 在拷贝和赋值时会转移所有权,这可能导致潜在的内存泄漏和未定义行为,因此在 C++11 中已经被废弃,推荐使用 std::unique_ptr 替代。

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass(const std::string& name) : name(name) {
        std::cout << "Constructing MyClass object with name: " << name << std::endl;
    }

    ~MyClass() {
        std::cout << "Destructing MyClass object with name: " << name << std::endl;
    }

    void printName() const {
        std::cout << "My name is: " << name << std::endl;
    }

private:
    std::string name;
};

int main() {
    std::auto_ptr<MyClass> ptr1(new MyClass("Object 1"));
    std::auto_ptr<MyClass> ptr2;

    ptr2 = ptr1; // Transfer ownership

    std::cout << "Using ptr2:" << std::endl;
    ptr2->printName();

    // At this point, ptr1 no longer owns the object
    // So, trying to use ptr1 may lead to unexpected behavior

    return 0;
}

在这个例子中,我们创建了一个名为MyClass的简单类,它具有构造函数和析构函数以及一个打印名称的成员函数。然后在main函数中,我们使用std::auto_ptr来管理MyClass对象的所有权。我们创建了两个auto_ptr指针ptr1ptr2,并且在将ptr1指向的对象的所有权转移给ptr2后,ptr1不再拥有对象。这种所有权转移意味着在程序的后续部分,只能使用ptr2来访问和操作MyClass对象。

请注意,尽管这个例子展示了std::auto_ptr的使用方式,但它已经被标记为废弃,因为它存在一些问题,比如它不适用于数组和可能会导致潜在的内存泄漏。在实际项目中,应该使用std::unique_ptr或其他更现代的智能指针替代std::auto_ptr

2. std::unique_ptr

std::unique_ptr 提供了独占所有权的智能指针。一个 std::unique_ptr 指针是唯一拥有其所指对象的指针,当 std::unique_ptr 被销毁时,它所指的对象也被销毁。这使得 std::unique_ptr 对象在整个程序中拥有唯一性。std::unique_ptr 是轻量级的,因为它只包含一个指向所分配对象的指针。

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass(const std::string& name) : name(name) {
        std::cout << "Constructing MyClass object with name: " << name << std::endl;
    }

    ~MyClass() {
        std::cout << "Destructing MyClass object with name: " << name << std::endl;
    }

    void printName() const {
        std::cout << "My name is: " << name << std::endl;
    }

private:
    std::string name;
};

int main() {
    std::unique_ptr<MyClass> ptr(new MyClass("Object 1"));

    ptr->printName();

    return 0;
}

在这个例子中,我们同样创建了一个名为MyClass的简单类,然后使用std::unique_ptr来管理MyClass对象的所有权。与std::auto_ptr不同,std::unique_ptr提供了更安全和更灵活的所有权管理。当unique_ptr超出作用域时,它会自动释放所管理的对象,因此不需要手动释放。

在使用std::unique_ptr时需要注意:

  1. 不要使用裸指针访问unique_ptr管理的对象,应该始终通过unique_ptr来访问。这样可以确保对象的所有权不会在不经意间转移。
  2. 不要使用delete来释放unique_ptr管理的对象,因为unique_ptr会自动管理对象的生命周期。
  3. 避免不必要地使用std::move来转移unique_ptr的所有权,除非你清楚地知道你在做什么。unique_ptr的所有权转移是一种显式操作,应该慎重使用。

通过遵循这些注意事项,可以更有效地利用std::unique_ptr来管理动态分配的对象,并防止常见的内存管理错误。

3. std::shared_ptr

std::shared_ptr 提供了共享所有权的智能指针。多个 std::shared_ptr 可以指向同一个对象,当最后一个 std::shared_ptr 被销毁时,才会销毁所指对象。std::shared_ptr 使用引用计数来跟踪有多少个指针共享同一对象,从而在不再需要时正确地释放资源。

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass(const std::string& name) : name(name) {
        std::cout << "Constructing MyClass object with name: " << name << std::endl;
    }

    ~MyClass() {
        std::cout << "Destructing MyClass object with name: " << name << std::endl;
    }

    void printName() const {
        std::cout << "My name is: " << name << std::endl;
    }

private:
    std::string name;
};

int main() {
    std::shared_ptr<MyClass> ptr1(new MyClass("Object 1"));

    {
        std::shared_ptr<MyClass> ptr2 = ptr1;

        ptr1->printName();
        ptr2->printName();
    }


    return 0;
}

在这个例子中,我们同样创建了一个名为MyClass的简单类,然后使用std::shared_ptr来管理MyClass对象的所有权。与std::unique_ptr不同,std::shared_ptr允许多个指针共享对同一对象的所有权。当最后一个shared_ptr超出作用域时,它会销毁对象。

在使用std::shared_ptr时需要注意:

  1. 使用shared_ptr时要注意循环引用的问题,这可能导致内存泄漏。循环引用指的是两个或多个对象相互持有对方的shared_ptr,导致它们的引用计数永远不会归零,从而对象无法被正确释放。
  2. 避免在多线程环境下对同一shared_ptr进行并发修改,需要使用适当的同步机制来保护共享资源,以避免数据竞争。
  3. 避免将裸指针直接转换为shared_ptr,应该使用std::make_shared或者shared_ptr的构造函数来创建shared_ptr对象,这样可以确保资源的安全管理。

4. std::weak_ptr

std::weak_ptr 是一种不拥有对象的共享指针,它用于解决 std::shared_ptr 的循环引用问题。std::weak_ptr 允许你观察 std::shared_ptr 指向的对象,但不会增加引用计数。因此,使用 std::weak_ptr 可以避免循环引用导致的内存泄漏。

#include <iostream>
#include <memory>
 
class MyClass {
public:
    MyClass(const std::string& name) : name(name) {
        std::cout << "Constructing MyClass object with name: " << name << std::endl;
    }
 
    ~MyClass() {
        std::cout << "Destructing MyClass object with name: " << name << std::endl;
    }
 
    void printName() const {
        std::cout << "My name is: " << name << std::endl;
    }
 
private:
    std::string name;
};
 
// 定义一个函数,该函数接受一个weak_ptr作为参数,并尝试将其转换为shared_ptr,然后访问其所管理的对象
void accessWeakPtr(std::weak_ptr<MyClass> weakPtr) {
    if (auto sharedPtr = weakPtr.lock()) {
        sharedPtr->printName();
    } else {
        std::cout << "Weak pointer expired." << std::endl;
    }
}
 
int main() {
    std::shared_ptr<MyClass> ptr(new MyClass("Object 1"));
    std::weak_ptr<MyClass> weakPtr = ptr;
 
    accessWeakPtr(weakPtr);
 
 
    return 0;
}

在这个例子中,创建了一个名为MyClass的简单类,并在main函数中使用std::shared_ptr管理MyClass对象的所有权。然后,我们创建了一个std::weak_ptr指向同一个对象,并将其传递给accessWeakPtr函数。在accessWeakPtr函数中,我们使用weak_ptr.lock()来尝试将weak_ptr转换为shared_ptr,然后访问其所管理的对象。

在使用std::weak_ptr时需要注意的事项有:

  1. 当使用weak_ptr.lock()时,需要检查返回的shared_ptr是否有效,因为如果原始的shared_ptr已经被销毁,weak_ptr.lock()会返回一个空的shared_ptr
  2. 不要直接使用weak_ptr访问所管理的对象,而是应该先将其转换为shared_ptr再进行访问,以确保对象的生命周期得到正确管理。
  3. 使用weak_ptr可以避免循环引用导致的内存泄漏,因为它不会增加对象的引用计数,但需要注意在使用时要确保原始的shared_ptr仍然有效。
  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值