0

考虑一个填充缓冲区的典型函数:

const char* fillMyBuffer( const char* buf, int size );

假设这个函数用一些有用的数据填充缓冲区,我想在调用后几乎立即使用这些数据,然后我想摆脱缓冲区。

一种有效的方法是在堆栈上分配:

doStuff();

{
    char myBuf[BUF_LEN];
    const char* pBuf = fillMyBuffer( myBuf, BUF_LEN );

    processBuffer( pBuf );
}

doOtherStuff();

所以这对我的库来说很棒,因为缓冲区是在堆栈上分配的——基本上没有分配、使用和丢弃的成本。它持续包含大括号的整个范围。

但我有一个图书馆,我一直在做这种模式。我想稍微自动化一下。理想情况下,我想要如下所示的代码:

doStuff();

{
    // tricky - the returned buffer lasts the entire scope of the braces.
    const char* pBuf = fillMyBufferLocal();

    processBuffer( pBuf );
}

doOtherStuff();

但是如何实现呢?

我做了以下,这似乎有效,但我知道这违反了标准:

class localBuf
{
public:
    operator char* () { return &mBuf[0]; }
    char mBuf[BUF_LEN];   
};

#define fillMyBufferLocal()   fillMyBuffer( localBuf(), BUF_LEN );

实际上,缓冲区在包含大括号的整个生命周期中都在堆栈上持续存在。但是标准说对象只需要持续到函数返回。例如,从技术上讲,它就像我在函数内部的堆栈上分配缓冲区一样不安全。

有没有一种安全的方法来实现这一目标?

4

3 回答 3

3

我通常会推荐您的原始解决方案。它将缓冲区的分配与填充分开。但是,如果要实现此fillMyBufferLocal替代方案,则必须动态分配缓冲区并返回指向它的指针。当然,如果你返回一个指向动态分配内存的原始指针,那么以后应该销毁内存就很不清楚了。相反,返回一个封装适当所有权的智能指针:

std::unique_ptr<char[]> fillMyBufferLocal()
{
  std::unique_ptr<char[]> buffer(new char[BUF_LEN]);
  // Fill it
  return buffer;
}

然后你可以像这样使用它:

auto buffer = fillMyBufferLocal();
processBuffer(buffer.get());
于 2013-04-25T15:19:49.647 回答
2

我认为您不应该这样做。它只会使代码更难理解。

自动存储持续时间意味着当一个对象超出范围时,它就会被销毁。在这里,您想欺骗系统,使其行为类似于创建具有自动存储持续时间的对象(即在堆栈上分配),但不遵守相应的规则(即从fillMyBuffer().

在我看来,您可以做的最接近、有意义的事情是使用fillMyBuffer()可以重用的全局缓冲区,或者让该缓冲区成为static内部的变量fillMyBuffer()。例如:

template<int BUF_LEN = 255>
const char* fill_my_buffer()
{
    static char myBuf[BUF_LEN];
    // Fill...
    return myBuf;
}

但是,我强烈建议重新考虑您的要求,并且:

  • 继续使用您当前采用的解决方案(即在堆栈上透明分配);或者
  • 在内部动态分配缓冲区fillMyBuffer()并将 RAII 包装器(如 a unique_ptr)返回到此动态分配的缓冲区。

更新:

作为最后的绝望尝试,您可以定义一个宏来fill_my_buffer()为您进行分配和调用:

#define PREPARE_BUFFER(B, S) \
    char buffer[S]; \
    const char* B = fill_my_buffer(buffer, S);

然后你可以这样使用它:

PREPARE_BUFFER(pBuf, 256);
processBuffer(pBuf);
于 2013-04-25T15:20:19.897 回答
1

您可以编写一个包含基于堆栈的缓冲区并转换为 char const * 的类,例如

void processBuffer(char const * buffer);
char const * fillMyBuffer(char const * buffer, int size);
int const BUF_LEN = 123;

class Wrapper
{
public:
  Wrapper(char const * (*fill)(char const *, int))
  {
    fill(&m_buffer[0], m_buffer.size());
  }

  operator char const * () const { return &m_buffer[0]; }

private:
  std::array<char, BUF_LEN> m_buffer;
};


void foo()
{
  Wrapper wrapper(fillMyBuffer);

  processBuffer(wrapper);

}
于 2013-04-25T15:36:33.760 回答