【问题标题】:C++03 smart pointer with free()带有 free() 的 C++03 智能指针
【发布时间】:2017-01-10 13:33:41
【问题描述】:

我有一个需要转移所有权的 char 指针,如果可能的话,我宁愿不必自己处理它的生命周期。内存是使用 malloc 分配的(这里没有选择)。

因此,我正在寻找像 C++11 的 unique_ptr 这样的东西,它管理所有权并允许提供自定义的 Deleter

正如标题所暗示的,我无法访问 C++11 功能。据我所知,auto_ptr 不是一个观众,因为它调用delete 而不是free

在这种情况下是否有合适的智能指针,还是我必须自己管理内存的释放?

【问题讨论】:

  • 我猜你可以自己写(代码不多)
  • @UnholySheep 是的,如果已经有合适的替代方案,我宁愿不要
  • 您可能想阅读this old question and its answers。此外,考虑到您的要求,为这个特定问题编写自己的自定义包装类应该不会做太多工作。
  • 根据智能指针需要的智能程度,您可以编写一个结构,在 ctor 中接受您的 char*,然后在析构函数中免费调用。这将是一个轻量级的唯一指针。 (当然,您必须将复制 ctor 和复制分配声明为私有,然后不实现它们以避免复制)。 Ofc 您还必须复制移动功能,可能作为产生char* 指针的成员函数,然后确保在dtor 中不调用free
  • 您可以使用boost::shared_ptr。或者你可以自己写。

标签: c++ pointers smart-pointers c++03


【解决方案1】:

作为自己编写的替代方法,您可以引入对 Boost 的依赖并使用 boost::shared_ptr

但为了比较,这里是基于 malloc/free 的所有权转移指针的最小现成 C++11 及更高版本代码,例如 std::unique

template< class Type >
class My_ptr
{
private:
    Type* p_;

    My_ptr( My_ptr const& ) = delete;
    operator=( My_ptr const& ) -> My_ptr& = delete;

public:
    auto operator->() const
        -> Type*
    { return p; }

    auto operator*() const
        -> Type&
    { return *p; }

    ~My_ptr() { free( p_ ); }

    My_ptr( Type* p )
        : p_( p )
    {}

    My_ptr( My_ptr&& other )
        : p_( other.p_ )
    { other.p_ = nullptr; }
};

如您所见,C++11 中的代码并不多。

免责声明:编译器未看到上述代码。


在 C++03 中,主要问题是如何使从函数返回智能指针成为可能,允许一般的复制构造,这会造成严重破坏。

std::auto_ptr 使用的解决方案是使用具有隐式转换的中介指针载体类。这很复杂。我记得在编写(曾经被 Wikipedia 引用的)指针教程时,我在 Visual C++ 的 std::auto_ptr 实现中遇到了很多特质。

下面的代码,希望是有效的 C++03(使用 g++ -std=c++03 测试),而是基于程序员通过调用 as_movable 成员函数明确指示需要移动操作的位置。它使用volatile 作为一种标记,以确保当as_movable 的结果用作构造函数参数时,只有移动的构造函数才能适合。在 C++03 中使用volatile 作为标签的想法,尽管在完全不同的背景下,曾经由 Andrei Alexandrescu 提出;可能是他之前的其他人,但据我记得他的用法是我第一次遇到这个想法的地方。

异常安全定义了布局分配和解除分配运算符operator newoperator delete。特别是,此处定义的位置operator delete在相关类型的构造函数通过抛出异常指示失败时由new-表达式隐式调用。然后在重新抛出异常之前,使用此运算符释放内存。

#include <exception>    // std::terminate
#include <new>          // std::bad_alloc
#include <stddef.h>     // size_t
#include <stdlib.h>     // malloc, free, NULL

#define MY_NEW( type, args ) \
    ::new type args

#define MY_MALLOC( type, args ) \
    ::new( my::c_memory_management ) type args

namespace my {
    struct C_memory_management {};
    C_memory_management const c_memory_management = C_memory_management();
}  // namespace my

void*
    operator new( size_t const size, my::C_memory_management )
{
    void* result = malloc( size );
    if( not result ) { throw std::bad_alloc(); }
    return result;
}

// This operator is (only) called automatically by a new-expression where the
// constructor for the type, throws. After the call the exception is re-thrown.
void operator delete( void* const p, my::C_memory_management )
{
    free( p );
}

#ifdef SUPPORT_ARRAYS
    void*
        operator new[]( size_t const size, my::C_memory_management const cmm )
    {
        return operator new( size, cmm );
    }

    void operator delete[]( void* const p, my::C_memory_management const cmm )
    {
        operator delete( p, cmm );
    }
#endif

namespace my {
    template< class Referent >
    struct Destruction_via_delete_
    {
        static void destroy( Referent const* p )
        {
            try
            {
                delete p;
            }
            catch( ... )
            {
                std::terminate();
            }
        }
    };

    template< class Referent >
    struct Destruction_via_free_
    {
        static void destroy( Referent const* p )
        {
            try
            {
                p->~Referent();
            }
            catch( ... )
            {
                std::terminate();
            }
            ::free( const_cast<Referent*>( p ) );
        }
    };

    template< class Referent >
    class Auto_ptr_
    {
    public:
        typedef void Destruction_func( Referent const* );

    private:
        Auto_ptr_& operator=( Auto_ptr_ const& );   // No copy assignment.
        Auto_ptr_( Auto_ptr_ const& );              // No COPYING via copy constructor.
        // A non-const argument copy constructor, for moving, is defined below.

        Referent*           p_;
        Destruction_func*   destroy_func_;

        static void dummy_destroy_func( Referent const* ) {}

    public:
        Auto_ptr_ volatile&
            as_movable()
        { return const_cast<Auto_ptr_ volatile&>( *this ); }

        Referent*
            release()
        {
            Referent* result = p_;
            p_ = NULL;
            return p_;
        }

        Referent*
            operator->() const
        { return p_; }

        Referent&
            operator*() const
        { return *p_; }

        ~Auto_ptr_()
        { destroy_func_( p_ ); }

        Auto_ptr_()
            : p_( NULL )
            , destroy_func_( &dummy_destroy_func )
        {}

        explicit Auto_ptr_(
            Referent* const             p,
            Destruction_func* const     destroy_func    = &Destruction_via_delete_<Referent>::destroy
            )
            : p_( p )
            , destroy_func_( destroy_func )
        {}

        explicit Auto_ptr_(
            C_memory_management,        // tag
            Referent* const             p
            )
            : p_( p )
            , destroy_func_( &Destruction_via_free_<Referent>::destroy )
        {}

        // A C++03 emulation of move constructor; allows return of lvalue.
        Auto_ptr_( Auto_ptr_ volatile& other )
            : p_( other.p_ )
            , destroy_func_( other.destroy_func_ )
        {
            other.p_ = NULL;
            other.destroy_func_ = &dummy_destroy_func;
        }
    };

}  // namespace my

#include <stdio.h>

struct Blah
{
    char const* hello() const { return "Hello from Blah-land! :)"; }

    ~Blah() { printf( "<destroy>\n" ); }
    Blah() { printf( "<init>\n" ); }
};

my::Auto_ptr_< Blah >
    foo()
{
    using namespace my;
    Auto_ptr_< Blah  > p( c_memory_management, MY_MALLOC( Blah,() ) );
    return p.as_movable();
}

void bar( my::Auto_ptr_<Blah> const p )
{
    printf( "%s\n", p->hello() );
}

int main()
{
    my::Auto_ptr_<Blah> p = foo().as_movable();

    printf( "Calling bar()...\n" );
    bar( p.as_movable() );
    printf( "Returned from bar().\n" );
}

输出:

调用 bar()... 你好,来自 Blah-land! :) 从 bar() 返回。

免责声明:我没有为上面的代码编写任何单元测试,实际上唯一的测试是上面显示的,它有效。恕我直言,在生产代码中使用它需要测试人们希望它适用的各种情况。

【讨论】:

  • boost::shared_ptr 也是我的第一个想法。
  • 几乎没有。 auto 返回必须由适当的返回类型替换,&amp;&amp; 移动构造函数由移动仿真层替换,如果需要,= delete 由适当的私有非实现成员函数替换。
  • 删除答案,因为单个所有权指针对于 C++03 来说并不容易正确完成。例如。旧的 C++03 auto_ptr 在 Visual C++ 中表现得不稳定且不可预测。 无法删除,因为它已被接受。 ☹
  • 鉴于 OP 要求 C++03 解决方案,接受 C++11(及更高版本)的答案似乎有点奇怪。
  • 我想我将不得不做 C++03 的事情并修改答案。
猜你喜欢
  • 1970-01-01
  • 2021-12-26
  • 1970-01-01
  • 2020-03-01
  • 1970-01-01
  • 2010-10-04
  • 2013-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多