簡體   English   中英

避免 C++14 中的字段零初始化

[英]Avoid field zero initialization in C++14

由於 C++14(可能是 C++11,我不確定),在某些條件下,類構造會發生零初始化,具體取決於調用構造函數的方式。 有沒有辦法確保原始值字段(比方說指針)永遠不會被初始化為零?

我猜不是因為似乎零初始化發生在類級別而不是字段級別(肯定是一種memset(this, 0, sizeof(TheClass)) ),但我仍然希望有辦法,一個黑客, 某物...

其想法是能夠在調用放置 new 之前初始化一個字段,以便該成員在構建期間可用。

根據 cppreference 對 零初始化的看法:

如果 T 是非聯合類類型,則所有基類和非靜態數據成員都初始化為零,並且所有填充都初始化為零位。 構造函數(如果有)將被忽略。

如果您的對象是零初始化的受害者,那么您就不走運了。 並且每個具有靜態或線程本地存儲持續時間的對象將始終為零初始化(常量初始化除外)。

有沒有辦法確保原始值字段(比方說指針)永遠不會被初始化為零?

這是可能的,是的。

如果該類可以簡單地默認構造,則只需默認初始化對象,其成員也將默認初始化:

int main() {
    T t; // default initialised

如果該類不是普通的默認可構造函數,您可以編寫一個用戶定義的構造函數,使成員未初始化(請注意,通常不鼓勵這樣做):

struct S {
    int member;
    S(){} // member is left default initialised

警告:使用用戶定義的構造函數,即使對象是值初始化的,成員也會被默認初始化。


具有靜態存儲持續時間的對象始終為零或常量初始化。 這些都不會使成員未初始化。 避免初始化的唯一方法是不創建具有靜態存儲持續時間的對象。


其想法是能夠在調用放置 new 之前初始化一個字段,以便該成員在構建期間可用。

如果你的意思是你的想法是初始化一個尚未使用placement new 創建的對象的字段,那么這個想法是不可能實現的。

成員在施工時間可用。 你不需要任何技巧來實現這一目標。 可以由其兄弟姐妹按照初始化順序使用的所有成員。 並且在所有成員都初始化后運行構造函數體。

正如@eerorika 的回答所暗示的那樣:

struct S {
    int member;
    S(){} // member is left default initialised

...請注意,通常不鼓勵這樣做

我會說,在不混淆類用戶的情況下實現這一目標的最清晰方法不是使用int ,而是使用std::aligned_storage_t ,因為很明顯該成員僅用於存儲可能稍后創建的對象而不是可以單獨使用的值。

根據您想要編寫多少代碼,這可能會很乏味,因為您必須編寫放置 new並使用std::launder (c++17 起)才能符合標准。 但是,這是 IMO 表達您意圖的最佳方式。

cppreference 給出了如何使用std::aligned_storage一個很好的例子(有一些修改,見評論):

#include <iostream>
#include <type_traits>
#include <string>

template<class T, std::size_t N>
class static_vector
{
    // properly aligned uninitialized storage for N T's
    typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
    std::size_t m_size = 0;

public:
    // My modification here suppresses zero-initialization
    // if initialized with empty braces
    static_vector() {};

    // Create an object in aligned storage
    template<typename ...Args> void emplace_back(Args&&... args) 
    {
        if( m_size >= N ) // possible error handling
            throw std::bad_alloc{};

        // construct value in memory of aligned storage
        // using inplace operator new
        new(&data[m_size]) T(std::forward<Args>(args)...);
        ++m_size;
    }

    // Access an object in aligned storage
    const T& operator[](std::size_t pos) const 
    {
        // note: needs std::launder as of C++17
        return *reinterpret_cast<const T*>(&data[pos]);
    }

    // Delete objects from aligned storage
    ~static_vector() 
    {
        for(std::size_t pos = 0; pos < m_size; ++pos) {
            // note: needs std::launder as of C++17
            reinterpret_cast<T*>(&data[pos])->~T();
        }
    }
};

int main()
{
    static_vector<std::string, 10> v1;
    v1.emplace_back(5, '*');
    v1.emplace_back(10, '*');
    std::cout << v1[0] << '\n' << v1[1] << '\n';

    static_vector<std::size_t, 10> v2{};

    // This is undefined behavior.
    // Here it's just used to demonstrate that
    // the memory is not initialized.
    std::cout << v2[0] << "\n";
}

在編譯器資源管理器上運行它


然而,由於 c++ 標准只規定必須發生零初始化的時候,而不是必須發生零初始化的時候,有時你真的必須與你的編譯器搏斗以抑制零初始化。

有關更多詳細信息,請參閱此問題此問題

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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