[英]Using 'memcpy()' inside a class with a union
我有一個 class foo
,它使用小緩沖區優化 (SBO) 管理數據。
當size < 16
時,數據保存在本地(在buffer
中),否則存儲在堆上, reserved
保存分配的空間。
class foo {
static const int sbo_size = 16;
long size = 0;
char *ptr;
union {
char buffer[sbo_size];
long reserved;
};
public:
foo()
{
for (int i = 0; i < sbo_size; ++i)
buffer[i] = 0;
}
void clone(const foo &f)
{
// release 'ptr' if necessary
if (f.size < sbo_size)
{
memcpy(this, &f, sizeof(foo));
ptr = buffer;
} else
{
// handle non-sbo case
}
}
};
關於clone()
的問題:
對於 SBO 情況,編譯器可能不清楚將使用union::buffer
。
使用memcpy
並相應地設置ptr
是否正確?
如果您可以使用 C++17,我會通過使用std::variant
代替聯合來回避任何潛在的類型雙關問題。
雖然這在內部使用了少量存儲來跟蹤它包含的當前類型,但它可能是一個整體的勝利,因為你的ptr
變量可以消失(盡管它應該在你的union
中)。
它也是類型安全的, union
不是這樣(因為如果變量不包含所需的類型, std::get
將拋出異常)並且將通過簡單地分配給它來跟蹤它包含的數據類型。
生成的 class 片段可能看起來像這樣(毫無疑問,這段代碼可以改進):
class foo
{
private:
static const size_t sbo_size = 16;
using small_buf = std::array <char, sbo_size>;
size_t size = 0;
std::variant <small_buf, char *> buf = { };
public:
void clone (const foo &f)
{
char **bufptr = std::get_if <char *> (&buf);
if (bufptr)
delete [] *bufptr;
size = f.size;
if (size < sbo_size)
buf = std::get <small_buf> (f.buf);
else
{
buf = new char [size];
std::memcpy (std::get <char *> (buf), std::get <char *> (f.buf), size);
}
}
};
筆記:
你會看到我使用了std::array
而不是 C 風格的數組,因為std:array
有很多 C 風格的 arrays 沒有的好特性
為什么clone
而不是復制構造函數?
如果您希望foo
有一個empty
的 state (例如,在默認構造之后),那么您可以查看名稱奇怪的std::monostate
。
對於原始存儲, std::byte
可能優於char
。
完整的例子在這里。
編輯:為了回答提出的問題,我不是語言律師,但在我看來,在clone
內部,編譯器不知道f
的活躍成員可能是什么,因為它實際上是從外太空跳傘進來的.
在這種情況下,我希望編譯器編寫者謹慎行事,並將工會的活躍成員設置為“不知道”,直到出現一些具體信息。 但是(這是一個很大的但是),我不想賭我的襯衫。 這是一項復雜的工作,編譯器編寫者確實會犯錯誤。
因此,本着分享的精神,這里是對您的原始代碼稍作修改的版本,它修復了這個問題。 我也將ptr
移到你的聯盟中,因為它顯然屬於那里:
class foo {
static const int sbo_size = 16;
long size = 0;
union {
std::array <char, sbo_size> buffer; // changing this
char *ptr;
long reserved;
};
public:
foo()
{
for (int i = 0; i < sbo_size; ++i)
buffer[i] = 0;
}
void clone(const foo &f)
{
// release 'ptr' if necessary
if (f.size < sbo_size)
{
buffer = f.buffer; // lets me do this
ptr = buffer.data ();
} else
{
// handle non-sbo case
}
}
};
所以你可以看到,通過使用std::array
作為buffer
(而不是那些古怪的 C 風格數組之一),你可以直接分配給它(而不是必須求助於memcpy
)然后編譯器將使它成為活動的你的工會成員,你應該是安全的。
總之,這個問題實際上是毫無意義的,因為一個人不應該(永遠)需要編寫這樣的代碼。 但毫無疑問,有人會立即想出一些證明我錯了的東西。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.