2011-01-06 139 views
2

我出於好奇而不是困難問這個問題,因爲我總是從你那裏學習,甚至在無關的主題上。我可以將char *緩衝區轉換爲對象類型嗎?

所以,考慮下面的方法,用C++編寫,並用克++相連。這種方法工作正常,因爲一切都被初始化爲正確的大小。現在

extern "C" 
    { 
    void retrieveObject(int id, char * buffer) 
     { 
     Object::Object obj; 

     extractObject(id, obj); 
     memcpy(buffer, &obj, sizeof(obj)); 
     } 
    } 

// Prototype of extractObject 
const bool extractObject(const int& id, Object::Object& obj) const; 

,我想避免本地Object和使用memcpy的聲明。

我試過的東西,如更換retrieveObject

void retrieveObject(int id, char * buffer) 
    { 
    // Also tried dynamic_cast and C-Style cast 
    extractObject(id, *(reinterpret_cast<Object::Object *>(buffer))); 
    } 

它編譯和鏈接成功,但崩潰的時候了。考慮到我的緩衝區足夠容納一個Object,C++是否需要調用構造函數來「塑造」內存?是否有另一種方法來取代局部變量和memcpy?

我希望我是很清晰爲你解答,謝謝提前。

+0

問題是,你爲什麼要這樣做?這些東西在C++中幾乎沒有必要。 (如果您正在序列化爲文件或網絡通信,則不是這樣。) – Thanatos 2011-01-06 10:45:36

+0

對象創建應該涉及構造函數。在構造函數中使用緩衝區。 – DumbCoder 2011-01-06 10:46:09

+0

我想知道爲什麼extractObject()的返回類型是`const bool`,爲什麼它說'const int&`是它的一個參數。有什麼優勢? – Nawaz 2011-01-06 10:50:42

回答

3

在你的第一次努力......

void retrieveObject(int id, char * buffer) 
{ 
    Object::Object obj; 
    extractObject(id, obj); 
    memcpy(buffer, &obj, sizeof(obj)); 
} 

...你仍然有編譯器創建局部變量OBJ,保證正確對準。在第二個努力...

void retrieveObject(int id, char * buffer) 
{ 
    extractObject(id, *(reinterpret_cast<Object::Object *>(buffer))); 
} 

...你很有希望編譯器的緩衝區指向一個字節,適當對於一個Object :: Object。但會是嗎?可能不會,因爲運行時崩潰。一般來說,char *可以在任何給定字節上開始,因爲更復雜的對象通常與字大小對齊,或者與其數據成員需要的最大對齊對齊。在Object :: Object內讀取/寫入整數,雙精度值,指針等僅當內存正確對齊時才起作用 - 它取決於CPU等等,但在UNIX/Linux上,未對齊可能產生例如一個SIGBUS或SIGSEGV信號。

爲了解釋這一點,我們來考慮一個簡單的CPU /內存架構。假設內存允許在任何給定的操作中從地址0-3,4-7或8-11等中讀取4個字節(32位體系結構),但不能在地址處讀取4字節卡盤1-4,2-5,3-6,5-8 ....聽起來很奇怪,但這實際上是記憶的一個常見限制,所以只要接受它並考慮後果即可。如果我們想在內存中讀取一個4字節的數字 - 如果它位於4個地址中的一個,我們可以在一次內存讀取中讀取它,否則我們必須讀取兩次:從一個包含部分數據,然後是包含其餘的4字節區域,然後丟棄我們不想要的位,並將剩餘的位重新組合到適當的位置以將32位值存入CPU寄存器/存儲器。這太慢了,所以語言通常會謹慎地把我們想要的值放在內存可以在一個操作中訪問它們的地方。即使CPU是按照這種期望設計的,因爲它們通常具有直接在存儲器中操作值的指令,而沒有將它們明確地加載到寄存器中(即,甚至在組裝/機器代碼級別下的實現細節)。要求CPU對未對齊的數據進行操作的代碼通常會導致CPU生成中斷,操作系統可能會將其顯示爲信號。

也就是說,關於在非POD數據上使用它的安全性的其他警告也是有效的。

1

那麼這可能有很多問題 - 首先,如果你使用本地對象,你不能只是構造它,然後在其上寫一些其他實例的內存(這隻適用於POD類型,因爲它們不需要調用析構函數),否則你可能會發生令人討厭的內存泄漏。

但是,這不是主要的問題 - 你曾經提供的解決方案可能或可能無法正常工作的基礎上,使用的對象的類型。它可以用於簡單的POD類型,它甚至可以用於更復雜的類(假設你將正確地處理構造函數/析構函數調用),但是在程序的其他部分期望對象處於原始狀態位置 - 讓我們說,你有一個類,有2個成員變量:

struct A { 
    int i; 
    int * pi; 
} 

在「圓周率」總是指向「我」的成員 - 如果你「的memcpy」該對象到其他位置,它很容易破裂。

+0

感謝您關注內存泄漏。我知道這很糟糕,它在90%的案例中不起作用,但就我而言,這很有效,因爲我的結構足夠簡單。 – 2011-01-06 12:07:37

2

你正在做的事情是有效地序列化Object並且將正常工作當且僅當Object中的所有數據都連續存儲。對於簡單的對象,這將工作正常,但只要有對象包含指向其他對象的指針,就會停止工作。

在C++中,對象包含其他對象是非常普遍的。 std::string就是一個例子。 string類是引用其他地方存儲的引用計數器對象的容器。所以除非你確定對象是一個簡單的連續對象,否則不要這樣做。

+0

這一切都是有效的,但與實際觀察到的行爲(崩潰)無關,這可能是由於緩衝區未對齊Object :: Object ...而引起的。 – 2011-01-06 12:00:34

1

找出崩潰的原因和位置,使用調試器。代碼看起來不錯。

如果你想避免中間對象實例,那麼簡單地避免它。使extractObject()返回一個指向Object的指針,並使用該指針memcpy()將其內容寫入buffer

不過要小心,正如其他人所說的,如果你那麼只是reinterpret_cast<>buffer回到Object如果Object不夠簡單,可能會破壞Object。

相關問題