如何在作用域指针类中正确使用动态分配的不透明指针?

时间:2011-08-09 00:01:13

标签: c++ casting smart-pointers opaque-pointers

背景

我正在使用英特尔IPP加密库进行测试。

它们定义了几个用于共享上下文的不透明结构,例如散列和加密,当然,这些结构不能直接实例化。

要初始化其中一个不透明的结构,请查询字节大小,然后动态分配一些字节并转换为结构指针。

他们的例子是这样的:

int byteSize = 0;
ippsSHA256GetSize(&byteSize);

// IppsSHA256State shaCtx; // Error: incomplete type is not allowed
IppsSHA256State * shaCtx = (IppsSHA256State *)(new uint8_t[byteSize]);
// use shaCtx
delete [] (uint8_t *)shaCtx;

问题

在scoped指针类中包装它的正确方法是什么,以便我不必担心重新分配?


我尝试过的事情

我认为以下内容不安全,因为析构函数中对 delete 的调用将是T类型,而不是数组上的 delete [] 实际上已分配:

boost::scoped_ptr<IppsSHA256State> ctx(
    reinterpret_cast<IppsSHA256State *>(new uint8_t[byteSize])
    );

我考虑的另一个(简化)选项是我自己的一个简单的作用域指针类,但是那里的转换使我不确定这是否正确,尽管我对 reinterpret_cast 的理解是回到原始类型,不应该有任何歧义:

template <typename T>
class IppsScopedState
{
public:
    explicit IppsScopedState(size_t byteSize)
        : _ptr(reinterpret_cast<T *>(new uint8_t[byteSize]))
    {}

    T * get() const { return _ptr; }

    ~IppsScopedState(void) {
        if (_ptr) delete [] reinterpret_cast<uint8_t *>(_ptr);
    }
private:
    T * _ptr;
    //NUKE_COPYASSIGN_CONSTRUCTORS
};

最后,我考虑过上面的一点点变化:

template <typename T>
class IppsScopedState
{
public:
    explicit IppsScopedState(size_t byteSize)
        : _ptr(new uint8_t[byteSize])
    {}

    T * get() const { return reinterpret_cast<T *>(_ptr); }

    ~IppsScopedState(void) {
        if (_ptr) delete [] _ptr;
    }
private:
    uint8_t * _ptr;
    //NUKE_COPYASSIGN_CONSTRUCTORS
};

在任何一种情况下,用法都是这样的:

IppsScopedState<IppsSHA256State> ctx(byteSize); // after querying for the byteSize, of course

2 个答案:

答案 0 :(得分:0)

您可以使用boost::scoped_arraystd::vector

答案 1 :(得分:0)

你是对的:

boost::scoped_ptr<IppsSHA256State> ctx(
    reinterpret_cast<IppsSHA256State *>(new uint8_t[byteSize])
);

这是一个坏主意,原因有两个:delete将被调用而不是delete[],它将删除错误的类型。

boost::scoped_array应该可以正常工作:

boost::scoped_array<uint8_t> temparray (new uint8_t[byteSize]);
IppsSHA256State * shaCtx = (IppsSHA256State *)(temparray.get());

但是这会给你两个局部变量,你必须记住在完成shaCtx之后不要保持temparray。因此,滚动自己的包装器是一个非常有吸引力的选择,因为它为您提供了安全性。

您当前的IppsScopedState没问题,但我建议稍微调整一下,在内部使用boost :: scoped_array,添加operator->访问器和const访问:

template <typename T>
class IppsScopedState
{
public:
    explicit IppsScopedState(size_t byteSize)
        : _ptr(new uint8_t[byteSize])
    {}

    const T* operator->() const { return get(); }
    T* operator->() { return get(); }
    const T* get() const  { return reinterpret_cast<const T*> (_ptr.get()); }
    T* get()   { return reinterpret_cast<T*>(_ptr.get()); }

private:
    boost::scoped_array<uint8_t> _ptr;
    //NUKE_COPYASSIGN_CONSTRUCTORS
};

然后您可以轻松使用此包装器:

IppsScopedState<IppsSHA256State> shaCtx (byteSize);
shaCtx->member;  // access any member of IppsSHA256State
some_function(shaCtx.get());  // pass the IppsSHA256State* to a method

根据您的需要,operator->和const get()可能过度且不必要。

所以,最后,我推荐你的自定义包装器,并使用reinterpret_cast而不是旧的c风格的转换语法。