考虑一个填充缓冲区的典型函数:
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 );
实际上,缓冲器在包含支架的整个寿命期间在堆叠上持久。但是标准说该对象只需要持续到函数返回。例如。从技术上讲,它就像我在函数内部的堆栈上分配缓冲区一样不安全。
有没有一种安全的方法来实现这一目标?
答案 0 :(得分: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());
答案 1 :(得分:2)
我认为你不应该这样做。它只是让代码更难理解。
自动存储持续时间意味着当对象超出范围时,它将被销毁。在这里,您希望将系统欺骗为行为,就像创建具有自动存储持续时间的对象(即在堆栈上分配),但不遵守相应的规则(即从{{1返回时不被破坏) }})。
在我看来,你可以做的最接近,最有意义的事情是使用fillMyBuffer()
可以重用的全局缓冲区,或者让缓冲区成为fillMyBuffer()
内的static
变量。例如:
fillMyBuffer()
但是,我强烈建议您重新考虑您的要求,并且:
template<int BUF_LEN = 255>
const char* fill_my_buffer()
{
static char myBuf[BUF_LEN];
// Fill...
return myBuf;
}
内动态分配缓冲区,并将RAII包装器(如fillMyBuffer()
)返回到此动态分配的缓冲区。<强>更新强>
作为最后的绝望尝试,您可以定义一个宏来为您分配和调用unique_ptr
:
fill_my_buffer()
然后你会这样使用它:
#define PREPARE_BUFFER(B, S) \
char buffer[S]; \
const char* B = fill_my_buffer(buffer, S);
答案 2 :(得分: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);
}