c  智能指针使用不当导致内存泄漏问题解析-kb88凯时官网登录

来自:网络
时间:2024-09-10
阅读:
免费资源网,https://freexyz.cn/

shared_ptr相互嵌套导致循环引用

代码示例

#include 
#include 
using namespace std;
class b;
class a {
public:
    std::shared_ptr b_ptr;
    ~a() { std::cout << "a destroyed\n"; }
};
class b {
public:
    std::shared_ptr

解释说明

  • 创建了两个 std::shared_ptr 对象 a 和 b
  • a 持有 b 的 shared_ptrb 持有 a 的 shared_ptr
  • 当 main 函数结束时,a 和 b 的引用计数不会减少到零,因此它们的析构函数不会被调用。
  • 导致内存泄漏,因为对象 a 和 b 的内存不会被释放。  

解决方法

为了避免这种循环引用的问题,可以使用 std::weak_ptrstd::weak_ptr 是一种弱智能指针,它不会增加对象的引用计数。它可以用来打破循环引用,从而防止内存泄漏。

#include 
#include 
using namespace std;
class b;  // 先声明类 b,使得 a 和 b 可以互相引用。
class a {
public:
    std::shared_ptr b_ptr; // a 拥有 b 的强引用
    ~a() { std::cout << "a destroyed\n"; }
};
class b {
public:
    std::weak_ptr

shared_ptr的层次使用没有导致循环引用

shared_ptr>>>> jsfiles;

这个声明表示 jsfiles 是一个 std::shared_ptr,它指向一个 std::vector,向量中的每个元素是一个 std::shared_ptr,指向一个 std::pair 对象,而这个 std::pair 对象中包含一个 std::string 和一个 std::shared_ptr。它们之间只是层次结构,没有跨层次的相互引用 。也就是说没有内存泄漏的问题。证明如下:

#include 
#include 
#include 
#include 
using namespace std;
// 自定义 string 类,模拟 std::string
class mystring {
public:
    std::string data;
    mystring(const std::string& str) : data(str) {
        std::cout << "mystring created: " << data << std::endl;
    }
    ~mystring() {
        std::cout << "mystring destroyed: " << data << std::endl;
    }
    // 添加输出操作符重载
    friend std::ostream& operator<<(std::ostream& os, const mystring& mystr) {
        os << mystr.data;
        return os;
    }
};
// 自定义 pair 类,模拟 std::pair
template
class mypair {
public:
    k first;
    v second;
    mypair(const k& key, const v& value) : first(key), second(value) {
        std::cout << "mypair created: {" << first << ", " << *second << "}" << std::endl;
    }
    ~mypair() {
        std::cout << "mypair destroyed: {" << first << ", " << *second << "}" << std::endl;
    }
};
int main() {
    // 创建 jsfiles,它是一个 shared_ptr,指向 vector
    auto jsfiles = std::make_shared>>>>();
    // 添加元素
    auto innerpair1 = std::make_shared>>("file1", std::make_shared("content of file1"));
    auto innerpair2 = std::make_shared>>("file2", std::make_shared("content of file2"));
    jsfiles->push_back(innerpair1);
    jsfiles->push_back(innerpair2);
    // 访问元素
    for (const auto& pairptr : *jsfiles) {
        std::cout << "filename: " << pairptr->first << ", content: " << *pairptr->second << std::endl;
    }
    // 离开作用域时,智能指针会自动销毁它们管理的对象
    return 0;
}

c   智能指针使用不当导致内存泄漏问题解析

同时也证明了一个结论,构造函数和析构函数的调用顺序是相反的。 

回调函数中的循环引用问题

值捕获

#include 
#include 
#include 
class myclass {
public:
    myclass() { std::cout << "myclass created" << std::endl; }
    ~myclass() { std::cout << "myclass destroyed" << std::endl; }
    void setcallback(std::function cb) {
        callback_ = cb;
    }
    void executecallback() {
        if (callback_) {
            callback_();
        }
    }
private:
    std::function callback_;
};
void createnoleak() {
    auto myobject = std::make_shared();
    myobject->setcallback([=]() {
        std::cout << "callback executed, myobject use count: " << myobject.use_count() << std::endl;
    });
    myobject->executecallback();
}
int main() {
    createnoleak();
    std::cout << "end of program" << std::endl;
    return 0;
}

c   智能指针使用不当导致内存泄漏问题解析

可以看出myobject最后没有调用析构函数,是shared_ptr循环引用了。

引用捕获

如果换为引用捕获,则不会造成 shared_ptr循环引用。虽然这种方式不会增加引用计数,但需要特别注意捕获对象的生命周期,防止在 lambda 被调用时,对象已经被销毁,从而导致未定义行为。

c   智能指针使用不当导致内存泄漏问题解析

如何解决 

#include 
#include 
#include 
class myclass : public std::enable_shared_from_this {
public:
    myclass() { std::cout << "myclass created" << std::endl; }
    ~myclass() { std::cout << "myclass destroyed" << std::endl; }
    void setcallback(std::function cb) {
        callback_ = cb;
    }
    void executecallback() {
        if (callback_) {
            callback_();
        }
    }
private:
    std::function callback_;
};
void createnoleak() {
    auto myobject = std::make_shared();
    std::weak_ptr weakptr = myobject;
    myobject->setcallback([weakptr]() {
        if (auto sharedptr = weakptr.lock()) {
            std::cout << "callback executed, object is valid" << std::endl;
        } else {
            std::cout << "object already destroyed" << std::endl;
        }
    });
    myobject->executecallback();
    // 这里 myobject 是按 weak_ptr 捕获,当 createnoleak() 结束时,myobject 的生命周期也就结束了,并且引用计数=0
}
int main() {
    createnoleak();
    std::cout << "end of program" << std::endl;
    return 0;
}

c   智能指针使用不当导致内存泄漏问题解析

  • weakptr.lock() 的使用:持有 std::weak_ptr,并且需要检查或者使用其管理的对象。如果对象仍然存在(即它的 shared_ptr 引用计数大于零),我们希望获取一个 shared_ptr 来安全地使用该对象。否则,weak_ptr.lock() 返回一个空的 shared_ptr
  • std::enable_shared_from_this 是一个非常有用的标准库模板类,用于解决一个特定的问题: 当一个类的成员函数需要创建一个指向自己(this)的 std::shared_ptr 时,这类问题如何安全地实现。 std::enable_shared_from_this

背景问题

在使用 std::shared_ptr 管理对象时,有时会遇到需要在类的成员函数中获取该对象的 shared_ptr 的情况。例如,在一个类的成员函数中,如果想要得到一个指向该对象的 shared_ptr,不能简单地使用 std::shared_ptr(this),因为这会创建一个新的 shared_ptr,而不是增加现有的 shared_ptr 的引用计数。这可能导致对象被提前销毁或者多次销毁。

std::enable_shared_from_this 的作用

通过继承 std::enable_shared_from_this,类就能够安全地使用 shared_from_this 方法,从而获取一个 shared_ptr,该 shared_ptr 与其他 shared_ptr 共享所有权,而不会重复增加引用计数。

使用示例

#include 
#include 
// 定义 myclass 继承 std::enable_shared_from_this
class myclass : public std::enable_shared_from_this {
public:
    myclass() { std::cout << "myclass created" << std::endl; }
    ~myclass() { std::cout << "myclass destroyed" << std::endl; }
    // 一个成员函数,它需要返回一个指向自身的 shared_ptr
    std::shared_ptr getsharedptr() {
        // 使用 shared_from_this 返回一个 shared_ptr
        return shared_from_this();
    }
    void dosomething() {
        auto ptr = shared_from_this(); // 获取 shared_ptr
        std::cout << "doing something with myclass instance, ref count: " << ptr.use_count() << std::endl;
    }
};
void examplefunction() {
    // 创建 myclass 对象的 shared_ptr
    auto myobject = std::make_shared();
    // 调用成员函数获取 shared_ptr
    auto mysharedptr = myobject->getsharedptr();
    std::cout << "reference count after getsharedptr: " << mysharedptr.use_count() << std::endl;
    myobject->dosomething();
}
int main() {
    examplefunction();
    return 0;
}

注意

1.创建对象:
只有通过 std::shared_ptr 创建或管理的对象,才能安全地使用 shared_from_this

2. 保护避免使用 new 操作符:
直接使用 new 操作符创建的对象不能正确使用 shared_from_this,这样做可能会导致未定义行为(例如崩溃)。

为什么 std::enable_shared_from_this 是必要的?

std::enable_shared_from_this 内部维护了一个弱引用(std::weak_ptr)指向当前对象。这个弱引用确保不会增加引用计数,同时允许 shared_from_this 方法安全地获取 std::shared_ptr,从而真正共享管理的对象,避免不安全的重复引用计数增加。

通过这样做,c stl 提供了一种方便而安全的方式来管理对象的生命周期,特别是在需要从对象内部生成 shared_ptr的情境下。

总结

通过继承 std::enable_shared_from_thismyclass 能够安全地在其成员函数中创建返回指向自身的 std::shared_ptr,避免不必要的重复引用计数,从而有效地管理和共享对象生命周期。这样既提升了代码的安全性,也使得对象生命周期管理变得更加简洁和直观。

免费资源网,https://freexyz.cn/
返回顶部
顶部
网站地图