2012-10-15 56 views
6

我知道memset對class初始化是皺眉。例如,像下面這樣:如何在不打開vtbl的情況下完成memset(this,...)的等價操作?

class X { public: 
X() { memset(this, 0, sizeof(*this)) ; } 
... 
} ; 

會揍了vtbl如果有在驢友中有virtual功能。

我正在研究C-ish但以C++編譯的(冗長的)遺留代碼庫,因此所有有問題的成員通常都是POD,並且不需要傳統的C++構造函數。 C++的使用逐漸增多(像虛擬函數),這讓那些沒有意識到memset具有這些額外C++牙齒的開發人員咬牙咬牙。

我想知道是否有一個C++安全的方式來做一個初始的全部捕獲零初始化,可以通過特定的by-member初始化,其中零初始化不適當?

我找到類似的問題memset for initialization in C++zeroing derived struct using memset。這兩個都有「不使用memset()」的答案,但沒有好的選擇(尤其是可能包含許多成員的大型結構)。

+2

一般你把第一個字段的地址從那裏清除。毫無疑問,這違反了一些規則,但它總是對我有用。 –

+0

另一種選擇是爲班級編寫自己的alloc例程,這樣可以確保清除空間,但IIRC對此有限制。 –

+0

難道你不是指'memset(this,0,sizeof * this)'? (是的,這也會打破vtbl。) –

回答

1

您可以隨時在這些嵌入式結構中添加構造函數,這樣他們就可以清晰地說出來。

+0

這個問題真的是要在構造函數中放置什麼(不是,比如說100個賦值語句)。 –

1

這是可怕的,但你可能重載operator new/delete爲這些對象(或在一個共同的基類),並有實現提供zero'd出緩衝區。事情是這樣的:

class HideousBaseClass 
{ 
public: 
    void* operator new(size_t nSize) 
    { 
     void* p = malloc(nSize); 
     memset(p, 0, nSize); 
     return p; 
    } 
    void operator delete(void* p) 
    { 
     if(p) 
      free(p); 
    } 
}; 

人們還可以覆蓋全球new/delete運營商,但是這可能會產生負面影響PERF。

編輯:我剛剛意識到這種方法不適用於堆棧分配的對象。

+1

那不是_completely_醜陋的...... – AShelly

4

對於每個找到memset調用的類,添加一個memset成員函數,該函數忽略指針和大小參數,並對所有數據成員進行賦值。

編輯: 其實,這不應該忽視的指針,就應該把它比作this。在匹配時,爲對象做正確的事情,如果不匹配,則重新路由到全局函數。

+1

+1爲聰明 –

+0

是的,這有一些優點。更容易記住保持更新比管理5個不同構造函數中的列表。 –

1

試試這個:

template <class T> 
void reset(T& t) 
{ 
    t = T(); 
} 

這將歸零你的對象 - 無論是POD與否。

但不這樣做:

A::A() { reset(*this); } 

這將在無窮遞歸調用A::A

試試這個:

struct AData { ... all A members }; 
    class A { 
    public: 
     A() { reset(data); } 
    private: 
     AData data; 
    }; 
+0

這是什麼提升value_initialized: http://www.boost.org/doc/libs/1_38_0/libs/utility/value_init.htm 在做什麼? –

+0

@PeeterJoot它或多或少都有。實際上,它以更復雜的方式實現它,這個boost庫聲稱由於一些編譯器問題,並且由於我的答案中提供的方式並不總是對非POD類型最有效的方式,所以它使用靜態常量變量來實現此目的。但是,如果您尋求更換memset,我的「簡化」方式應該足夠了。 – PiotrNycz

+0

't = T();'這不會將對象歸零,它會用默認構造的替換它。如果默認構造函數未初始化成員,則該成員仍將保持未初始化狀態。如果它將某些東西初始化爲非零值,它也將保留在該值。重置爲_default_而不是_all零_實際上可能是一件好事,但它們與您的建議不一樣。 – Shahbaz

0

您可以使用指針運算,找到您要置零字節的範圍:

class Thing { 
public: 
    Thing() { 
     memset(&data1, 0, (char*)&lastdata - (char*)&data1 + sizeof(lastdata)); 
    } 
private: 
    int data1; 
    int data2; 
    int data3; 
    // ... 
    int lastdata; 
}; 

(編輯:我最初使用offsetof()這一點,但一評論指出,這隻適用於PODs,然後我意識到你可以直接使用成員地址。)

+0

'offsetof'只保證在_POD_類型上工作 –

0

更好的解決方案,我可以fi nd是創建一個分離的結構,你將把必須被memset的成員置零。不知道這個設計是否適合你。

這個結構沒有vtable和擴展nothings。它將只是一大塊數據。這種memsetting結構是安全的。

我已作出例如:

#include <iostream> 
#include <cstring> 

struct X_c_stuff { 
    X_c_stuff() { 
     memset(this,0,sizeof(this)); 
    } 
    int cMember; 
}; 
class X : private X_c_stuff{ 
public: 
    X() 
    : normalMember(3) 
    { 
     std::cout << cMember << normalMember << std::endl; 
    } 
private: 
    int normalMember; 
}; 

int main() { 
    X a; 
    return 0; 
} 
1

槓桿的事實,一個靜態實例被初始化爲零: https://ideone.com/GEFKG0

template <class T> 
struct clearable 
{ 
    void clear() 
    { 
     static T _clear; 
     *((T*)this) = _clear; 
    }; 
}; 

class test : public clearable<test> 
{ 
    public: 
     int a; 
}; 

int main() 
{ 
    test _test; 
    _test.a=3; 
    _test.clear(); 

    printf("%d", _test.a); 

    return 0; 
} 

然而上述將使構造(該templatised類的)被稱爲第二次。

對於不導致構造函數調用解決這個可以用來代替:https://ideone.com/S1ae8G

:如果你使用C++ 11日起 https://ideone.com/qTO6ka

template <class T> 
struct clearable 
{ 
    void *cleared; 
    clearable():cleared(calloc(sizeof(T), 1)) {} 

    void clear() 
    { 
     *((T*)this) = *((T*)cleared); 
    }; 
}; 

...,可用於以下

template <class T> 
struct clearable 
{ 
    void clear() 
    { 
     *((T*)this) = {}; 
    }; 
}; 
相關問題