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



背景

我正在使用"英特尔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;

问题

将其封装在作用域指针类中的正确方法是什么,这样我就不必担心解除分配了


我尝试过的东西

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

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

我考虑过的另一个(简化的(选项是我自己的一个简单作用域指针类,但那里的强制转换让我不确定这是否正确,尽管我对relpret_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

您认为这是正确的:

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());

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

您当前的IppsScopedState很好,但我建议稍微调整一下,以便在内部使用boost::scope_array,添加operator->访问器和const-access:

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->和常量get()可能是多余的。

因此,最后,我建议您使用自定义包装器,并在旧的c样式转换语法上使用repret_cast。

您可以使用boost::scoped_array或仅使用std::vector

相关内容

  • 没有找到相关文章

最新更新