智能指针可以选择性地隐藏或重定向函数调用到它们包装的对象吗?

Posted

技术标签:

【中文标题】智能指针可以选择性地隐藏或重定向函数调用到它们包装的对象吗?【英文标题】:Can smart pointers selectively hide or re-direct function calls to the objects they are wrapping? 【发布时间】:2010-10-31 13:58:56 【问题描述】:

我正在处理一个项目,其中引用了某些对象,它与 COM 非常相似。无论如何,我们的项目确实有智能指针,可以减少为这些对象显式调用 Add() 和 Release() 的需要。问题是有时,开发人员仍然使用智能指针调用 Release()。

我正在寻找一种从智能指针调用 Release() 创建编译时或运行时错误的方法。编译时间对我来说似乎是不可能的。我以为我有一个运行时解决方案(见下面的代码),但它也不能完全编译。显然,使用 operator->() 后不允许隐式转换。

无论如何,谁能想到一种方法来完成我想要完成的事情?

非常感谢您的帮助!

凯文

#include <iostream>
#include <cassert>

using namespace std;

class A

public:
    void Add()
    
        cout << "A::Add" << endl;
    

    void Release()
    
        cout << "A::Release" << endl;
    

    void Foo()
    
        cout << "A::Foo" << endl;
    
;

template <class T>
class MySmartPtrHelper

    T* m_t;

public:

    MySmartPtrHelper(T* _t)
        : m_t(_t)
    
        m_t->Add(); 
    

    ~MySmartPtrHelper()
    
        m_t->Release(); 
    

    operator T&()
    
        return *m_t;
    

    void Add()
    
        cout << "MySmartPtrHelper::Add()" << endl;
        assert(false);
    

    void Release()
    
        cout << "MySmartPtrHelper::Release()" << endl;
        assert(false);
    
;

template <class T>
class MySmartPtr

    MySmartPtrHelper<T> m_helper;

public:

    MySmartPtr(T* _pT)
        : m_helper(_pT)
    
    

    MySmartPtrHelper<T>* operator->()
    
        return &m_helper;
    
;

int main()

    A a;

    MySmartPtr<A> pA(&a);

    pA->Foo(); // this currently fails to compile.  The compiler
               // complains that MySmartPtrHelper::Foo() doesn't exist.

    //pA->Release(); // this will correctly assert if uncommented.

    return 0;

【问题讨论】:

请用运算符新分配替换堆栈分配“A a”吗?这不会改变最初的问题,但会消除使用引用计数智能指针的典型严重错误之一。 【参考方案1】:

你不能这样做 - 一旦你重载了 operator -&gt; 你就会被卡住 - 重载的操作符会以同样的方式行事,而不管它右边的东西。

您可以将 Add() 和 Release() 方法声明为私有,并使智能指针成为引用计数类的朋友。

【讨论】:

这是我的长期目标。不过现在,我们有很多在引入智能指针之前的遗留代码。不幸的是,我们面临着迫在眉睫的最后期限,我要等到未来某个时候才能实施这个解决方案——希望不会是不确定的未来。【参考方案2】:

operator-&gt; 必须返回一个本身支持operator-&gt; 的指针或对象。它可以是递归的。你不能做的是让operator-&gt; 的行为根据-&gt; 右侧出现的内容而有所不同。

我想不出任何不涉及以某种方式复制指向对象的接口的方法,或者要求您创建从指向对象公开派生的对象,并将 Add 和 Release 隐藏并在派生类并使用 Base* pBase = pDerived; pBase-&gt;Add(); 技巧从智能指针调用 add 和 release。

【讨论】:

【参考方案3】:

我通过更改 MySmartPtr 中的重载运算符并在 MySmartPtrHelper 中添加一个重载运算符来使其工作:

#include <iostream>
#include <cassert>

using namespace std;

class A

public:
    void Add()
    
        cout << "A::Add" << endl;
    

    void Release()
    
        cout << "A::Release" << endl;
    

    void Foo()
    
        cout << "A::Foo" << endl;
    
;

template <class T>
class MySmartPtrHelper

    T* m_t;

public:

    MySmartPtrHelper(T* _t)
        : m_t(_t)
    
        m_t->Add(); 
    

    ~MySmartPtrHelper()
    
        m_t->Release(); 
    

    operator T&()
    
        return *m_t;
    

    T* operator->()
    
        return m_t;
    


    void Add()
    
        cout << "MySmartPtrHelper::Add()" << endl;
        assert(false);
    

    void Release()
    
        cout << "MySmartPtrHelper::Release()" << endl;
        assert(false);
    
;

template <class T>
class MySmartPtr

    MySmartPtrHelper<T> m_helper;

public:

    MySmartPtr(T* _pT)
        : m_helper(_pT)
    
    

    T* operator->()
    
        return m_helper.operator->();
    
;

int main()

    A a;

    MySmartPtr<A> pA(&a);

    pA->Foo(); 
    //pA->Release(); // this will correctly assert if uncommented.

    return 0;

输出:

macbook-2:~ $ ./a.out 
A::Add
A::Foo
A::Release

【讨论】:

将引用计数指针绑定到堆栈分配的对象有什么意义? 我不知道,这是他在上面发布的代码,但已修复,因此可以正常工作。 不,这不像我希望的那样工作。如果 pA->Release() 未注释,则没有断言。这个想法是将调用“重定向”到 Release()。【参考方案4】:

我建议您使用类似以下代码的内容。 你想要的是不可能的除非你愿意添加一个小约束:对象必须是可复制构造的(你不介意使用这种可能性)。在这种情况下,继承是一个不错的选择。

#include <iostream>
#include <cassert>

using namespace std;

template <class T>
class MySmartPtrHelper : public T


public:

    MySmartPtrHelper(T* _t)
        : m_t(*_t)
    
        delete _t;
        ((T*) this)->Add();
    

    ~MySmartPtrHelper()
    
        ((T*) this)->Release(); 
    

    void Add()
    
        cout << "MySmartPtrHelper::Add()" << endl;
        //will yield a compile-time error  
        BOOST_STATIC_ASSERT(false) 
    

    void Release()
    
        cout << "MySmartPtrHelper::Release()" << endl;
        //will yield a compile-time error  
        BOOST_STATIC_ASSERT(false) 
    
;

template <class T>
class MySmartPtr

   MySmartPtrHelper<T>* m_helper;
   // Uncomment if you want to use boost to manage memory
   // boost::shared_ptr<MySmartPtrHelper<T> > m_helper;

public:

    MySmartPtr(T* _pT)
        : m_helper(new MySmartPtrHelper<T>(_pT))
    
    

    MySmartPtrHelper<T>* operator->()
    
        return m_helper;
    
;

int main()

    MySmartPtr<A> pA(new A());

    pA->Foo();

    //pA->Release(); // this will correctly assert if uncommented.

    return 0;

【讨论】:

当你复制智能指针时,指向的对象也会被复制,这完全违背了拥有引用计数智能指针的目的。 你是对的,但我的解决方案仍然有效,只要 BOOST_STATIC_ASSERT 用于生成编译时错误。目的不是生成实际可用的代码,而是可以用来删除对 Add() 和 Release() 的无用调用。

以上是关于智能指针可以选择性地隐藏或重定向函数调用到它们包装的对象吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何管道或重定向 curl -v 的输出?

拦截 bash 脚本函数/系统调用并将它们包装到自定义函数中

什么是WinRT语言预测?

如何正确地将浮点指针从 C 库传递到其 C# 包装器

智能指针总结

C++ this 指针,函数调用中的隐藏参数