c++ - 在带有 union 的类中使用 'memcpy()'

标签 c++ memcpy unions

我有课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 来回避任何潜在的类型双关问题。代替 union 。

虽然这在内部使用了少量存储来跟踪它包含的当前类型,但总体而言这可能是一个胜利,因为您的 ptr变量可以消失(尽管它应该在你的 union 中)。

它也是类型安全的,即 union不是(因为如果变体不包含所需的类型, std::get 将抛出异常),并且只需分配给它即可跟踪它包含的数据类型。

生成的类片段可能看起来像这样(毫无疑问,这个代码可以改进):

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 风格数组所没有的优秀功能

  • 为什么clone而不是复制构造函数?

  • 如果你想要foo拥有 empty状态(例如,在默认构造之后),那么您可以查看奇怪命名的 std::monostate .

  • 对于原始存储, std::byte 可能比 char 更受青睐.

完整的示例 here .


编辑:为了回答所提出的问题,我不是语言律师,但在我看来,在 clone 内,编译器不知道 f 的事件成员是什么实际上,它可能是从外太空跳伞而来的。

在这种情况下,我希望编译器编写者能够谨慎行事,并将 union 体的活跃成员设置为“不知道”,直到出现一些具体信息。但是(这是一个很大的但是),我不想把我的衬衫押在这一点上。这是一项复杂的工作,编译器编写者确实会犯错误。

因此,本着分享的精神,这里对您的原始代码进行了稍微修改的版本,修复了这个问题。我也搬家了ptr在你的 union 内,因为它显然属于那里:

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 ),编译器将然后使其成为您 union 的活跃成员你应该是安全的。

总之,这个问题实际上毫无意义,因为人们不应该(永远)需要编写这样的代码。但毫无疑问有人会立即想出一些东西来证明我错了。

关于c++ - 在带有 union 的类中使用 'memcpy()',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71485076/

相关文章:

c++ - 要在 Excel 工作表上直接使用 ActiveX 控件,有哪些必要和充分的要求?

c++ - 获取当前目录的跨平台方式是什么?

c++ - 如何在 Windows 资源管理器中获取窗口的完整路径

c++ - 类内和类外的 friend 功能,有什么区别?

c# - 将数据从 C++ 传递到 C# 的最有效方法

c++ - 为什么将 char 数组复制到结构中时 memcpy 不起作用?

c - 使用通用函数将整数与短整数交换

c - 用于从 H/W 寄存器读取的位字段

c - 意外的 union 行为

linq union合并子列表