关于智能指针的问题 [英] A question regarding smart pointers

查看:68
本文介绍了关于智能指针的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我的程序中有以下代码结构

Lets say I have the following code structure in my program

class Base
{
public:
Base(){//dosomething}
~Base(){//dosomething} //Note: Destructor is not virtual
...
virtual void function() {//dosomething}
};

class Derived: public Base
{
public:
Derived(){//dosomething}
~Derived(){//dosomething}
...
virtual void function() {//dosomething}
};





现在我写了类似的代码





Now I write a code something like

void somefunction()
{
std::shared_ptr<Base> stdBasePtr(new Derived() );

//do something with the Smart Pointers
}





现在一些函数超出范围后,因为析构函数或Base不是虚拟的。我期待内存泄漏。

Q1。我的假设是否正确?

Q2。如果上述问题的答案是否定的。然后如何处理。

Q3。如果答案是肯定的。然后我可以在我的智能指针实现中编码来处理这种情况。



提前致谢



Now after some function goes out of scope, since the destructor or Base is not virtual. I am expecting a memory leak.
Q1. Is my assumption correct?
Q2. If the answer to question above is no. Then how is it handled.
Q3. If the answer is yes. Then what can I code in my implementation of smart pointer to handle such scenarios.

Thanks in advance

推荐答案

shared_ptr帮助您只分享所有权而无需任何东西你的班级,它不关心你的继承。 shared_ptr是非侵入式的,只知道Base类,所以它只删除你的ptr作为Base类指针。在这种情况下,您有责任通过将虚拟析构函数放入Base类来确保正确删除。确保正确删除指针类是不可能的。抱歉,我现在不对,它可以用一个模板构造函数来处理它,它在你的指针中存储了一个额外的删除对象(删除对象有虚拟析构函数而不是你的类)。但是,这比使用侵入式指针更容易出错。



使用模板ctor演示删除对象的用法:

shared_ptr helps you only in sharing ownership without putting anything into your class, it doesn't care about your inheritance. shared_ptr is non-intrusive and knows only about the Base class so it will delete your ptr only as a Base class pointer. Its your responsibility in this case to ensure correct deletion by putting a virtual destructor into your Base class. It would be impossible to ensure correct deletion the pointer class. Sorry, I wasn't right at this point, its possible to handle this with a template constructor that stores an additional deleter object inside your pointer (the deleter object has the virtual destructor instead of your class). Still, this is more error prone than using intrusive pointers most of the time.

Demonstrating the usage of a deleter object with a template ctor:
class Deleter
{
public:
    virtual ~Deleter() {}
};

template <typename Deletable>
class TDeleter : public Deleter
{
public:
    TDeleter(Deletable* p)
        : m_Ptr(p)
    {}
    ~TDeleter()
    {
        delete m_Ptr;
    }
private:
    Deletable* m_Ptr;
};


template <typename T>
class Ptr
{
public:
    template <typename U>
    Ptr(U* p)
    {
        m_Deleter = new TDeleter<U>(p);
        m_Ptr = p;
    }
    ~Ptr()
    {
        delete m_Deleter;
    }
    T* operator->()                      { return m_Ptr; }
    const T* operator->() const          { return m_Ptr; }
    T& operator*()                      { return *m_Ptr; }
    const T& operator*() const          { return *m_Ptr; }
private:
    Ptr(const Ptr&);
private:
    Deleter* m_Deleter;
    T* m_Ptr;
};


class C
{
public:
    C() { printf("%s\n", __FUNCTION__); }
    ~C() { printf("%s\n", __FUNCTION__); }
    void BaseMethod() { printf("%s\n", __FUNCTION__); }
};

class D : public C
{
public:
    D() { printf("%s\n", __FUNCTION__); }
    ~D() { printf("%s\n", __FUNCTION__); }
};

void TestPtr()
{
    Ptr<C> p(new D);
    p->BaseMethod();
}





我更喜欢侵入式共享指针。一个侵入式共享指针希望你的类自己处理它自己的引用计数,因此一个侵入式共享指针希望你的类有自己的AddRef / Release方法(或者你命名的任何方法)。因此,当侵入式共享指针获取对象的所有权时,它会调用其AddRef。当指针让对象消失时,它会调用它的释放方法。为了使这个工作,对象要么实现AddRef / Release本身,要么通常代替这个,你从已经实现AddRef / Release以及引用计数的基类派生你的对象,除此之外它还为你引入了一个虚拟析构函数,可以自动帮助你避免破坏虫子。这样一个侵入式共享指针可以很容易地实现,以便它很好地处理继承,自动upcast可以在没有问题的情况下在这些指针之间工作。另一个很好的功能可以是你在对象中使用的基类,你可以提供两个实现:一个用于单线程使用,另一个用于多线程使用,用于处理带有原子操作的引用计数(__sync_add_and_fetch,InterlockedXXX,.. 。)。



编辑:我通常只在我自己的代码库中使用以下指针:

- 侵入式共享指针(单/多线程)

- 侵入性弱指针

- 自动指针



注意一个对象可以是两个共享的目标/ weak指针,只要所有共享/弱指针引用来自同一个线程,因为在多线程的情况下使用弱指针没有意义。



编辑:两者都是侵入性的和非侵入性指针有其优点。当引用的类不是你的时,非侵入性非常有用。侵入性的那些不太容易将原始c ++指针从一个引用转移到另一个引用。我的经验是,后者可能导致很难找到错误,并且与侵入性ptrs一起工作的系统不那么错。这当然只是我的观察/意见。



I like intrusive shared pointers much more. An intrusive shared pointer expects your class to handle its own refcounting by itself so an intrusive shared pointer expects your class to have its own AddRef/Release methods (or whatever you name them). So when an intrusive shared pointer takes ownership of your object then it calls its AddRef. When the pointer lets the object go aways then it calls its release method. For this to work the object either implements AddRef/Release itself, or usually instead of this you derive your object from a base class that already implements AddRef/Release along with refcounting and besides this it introduces a virtual destructor for you that automatically helps you to avoid destruction bugs. Such an intrusive shared pointer can easily be implemented so that it handles inheritance well, automatic upcast can work between such pointers without problems. Another nice feature can be in case of the base class you use in your objects that you can provide two implementations: one for single threaded use, and another one for multithreaded use that handles the reference count with atomic operations (__sync_add_and_fetch, InterlockedXXX, ...).

I usually use only the following pointers in my own codebase:
- Intrusive shared pointer (single/multithreaded)
- Intrusive weak pointer
- auto pointer

Note that an object can be the target of both shared/weak pointers as long as all the shared/weak pointer references come from the same thread because using weak pointers in case of multithreading has no point.

Both intrusive and nonintrusive pointers have their advantages. Nonintrusives are very useful when the referenced classes are not yours. Intrusive ones are less vulnerable to transferring "raw" c++ pointers from one reference into another. My experience is that the latter thing can cause very hard to find bugs and systems that work with intrusive ptrs are less buggy. This is of course just my observation/opinion.


Q1:是和否。可能导致内存泄漏。但是一些编译器足够聪明,可以确定首先分配了多少内存并释放了适当数量的内存。但是考虑到Derived的析构函数不会被调用,这可能会产生其他负面影响,比如资源泄漏,阻塞锁定,或者再次发生二次内存泄漏。



Q2:你对潜在的内存泄漏是正确的。



Q3:最好的解决方案是让你的基类的析构函数虚拟化。如果你不能使用智能指针指向正确的类,在这种情况下std :: shared_ptr< Derived>。
Q1: Yes and no. A memory leak might result out of this. But some compilers are smart enough to figure out how much memory was allocated in the first place and free the right amount. But consider that the destructor of Derived will not be called, which might have other negative effects like resource leaks, blocking locks, or again a secondary memory leak.

Q2: You were right about the potential memory leak.

Q3: The best solution is to make the destructor of your base class virtual. If you can't use a smart pointer to the correct class, in this case std::shared_ptr<Derived>.


这篇关于关于智能指针的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆