簡體   English   中英

在帶有聯合的 class 中使用 'memcpy()'

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM