2010-06-07 95 views
1

我有存儲其輸出在一個特定類型的結構 「數據提供者」,例如C++如何深度複製未知數據類型的結構?

struct DATA_TYPE1{ 
    std::string data_string; 
}; 

然後這個結構必須被鑄造成一個普通的數據類型, 我考慮無效*或char *,因爲「中間」 對象將其複製並存儲在其二進制樹中,因此 能夠存儲許多不同類型的此類結構數據。

struct BINARY_TREE_ENTRY{ 
    void * DATA; 
    struct BINARY_TREE_ENTRY * next; 
}; 

這一空白*是後來被其它物體 是注塑無效*回(結構DATA_TYPE1 *) 得到原始數據。所以發件人和收件人 知道數據類型DATA_TYPE1,但不知道複製 中間的對象。

但中間對象如何在不知道數據類型的情況下深層複製 不同結構的內容, 只有void *且沒有方法來複制真實內容; dynamic_cast不適用於void *;

「中間」的對象應該這樣做:

void store_data(void * CASTED_DATA_STRUCT){ 
    void * DATA_COPY = create_a_deepcopy_of(CASTED_DATA_STRUCT); 
    push_into_bintree(DATA_COPY); 
} 

一個簡單的解決辦法是發送對象不 刪除發送的數據結構,直到接收對象得到它, 但發送對象是動態創建和刪除的,接收者從中間對象獲得數據之前, 進行異步通信,因此我想拷貝 它。

代替將其轉換到void *我也嘗試轉換 到一個超類指針,它的中間複製 對象知道,並且由所有不同 數據類型的結構的繼承:

struct DATA_BASE_OBJECT{ 
public: 
    DATA_BASE_OBJECT(){} 
DATA_BASE_OBJECT(DATA_BASE_OBJECT * old_ptr){ 
     std::cout << "this should be automatically overridden!" << std::endl; 
    } 
    virtual ~DATA_BASE_OBJECT(){} 
}; 

struct DATA_TYPE1 : public DATA_BASE_OBJECT { 
public: 
    string str; 
    DATA_TYPE1(){} 
    ~DATA_TYPE1(){} 
    DATA_TYPE1(DATA_TYPE1 * old_ptr){ 
     str = old_ptr->str; 
    } 
}; 
然後

和相應的二進制樹條目將是:

struct BINARY_TREE_ENTRY{ 
     struct DATA_BASE_OBJECT * DATA; 
     struct BINARY_TREE_ENTRY * next; 
    }; 

,並再複製未知的數據類型,我試圖在類 塔牛逼只是獲取未知的數據類型爲結構DATA_BASE_OBJECT * (之前它是無效*):

void * copy_data(DATA_BASE_OBJECT * data_that_i_get_in_the_sub_struct){ 
    struct DATA_BASE_OBJECT * copy_sub = new DATA_BASE_OBJECT(data_that_i_get_in_the_sub_struct); 
    push_into_bintree(copy_sub); 
} 

我再補充一個拷貝構造函數的DATA_BASE_OBJECT,但如果 的結構DATA_TYPE1首先澆鑄爲DATA_BASE_OBJECT然後複製 ,包含的子對象DATA_TYPE1也不會被複制。

我當時以爲怎麼樣找出實際的對象 的大小複製,然後就MEMCOPY,但字節並不存儲在 一行,我如何找出在 內存的實際大小struct DATA_TYPE1持有一個std :: string?

還有哪些其他C++方法可用於深度複製未知數據類型 (並且可能在運行時在 運行時以其他方式獲取數據類型信息)?

回答

9

如果您有void *,則無法從中提取任何類型信息。這就是爲什麼void *在C++程序中非常非常少用(我真的不記得上次使用它了) - 您在這裏的做法完全錯誤。如果您希望編譯時已知類型的泛型容器,請使用模板。如果您希望容器的類型在運行時發生變化,請從基類派生並使用基類指針的容器。不要寫自己的容器(除了可能作爲學習練習) - C++有一個完美的二叉樹,實現爲std :: set和std :: map。

最後,不要將全部大寫用於C++類型的名稱。

+0

+1打我19秒。 – Cam 2010-06-07 08:11:08

+0

爲了強調Neil在std中加入了什麼:如果你想用C++進行正確的代碼,std的良好使用是至關重要的。看起來好像你正在以一種非常類似C的方式進行編碼(例如你使用void指針),這通常不被認爲是好的C++。您應該查看stl並查看模板。 – Cam 2010-06-07 08:17:34

+2

阻止所有上限+1。我的眼睛! :-D – DevSolar 2010-06-07 08:17:38

0

無效指針在這裏不是一個好的解決方案,因爲它們在編譯時不能保證類型安全。

我建議使用模板。這將允許您仍然使用相同的類/函數處理不同的數據類型,並且還將保證類型安全性比void指針好得多。

編輯:爲進一步闡明爲什麼空指針甚至存在:空指針被用於這種東西。然而當你仍然可以在C++中使用它們,它通常是氣餒,因爲有更好的解決方案。

另外,你提到深拷貝。與數據庫一起使用的所有類型都必須實現deepCopy函數(這是一種OOP風格的方法),或者您可以簡單地記住,對於使用DATA_BASE的所有類型,賦值運算符都會超負荷:

0

但是中間對象如何在不知道數據類型的情況下深度複製不同結構的內容,只有void *並且沒有方法來複制實際內容; dynamic_cast不適用於void *;

你根本無法做到這一點。代表某些數據的void *塊必須包含多個指針(例如,std :: string動態分配內存)。而且您不知道它們的存儲位置,因此無法在不引起混亂的情況下深度複製數據。

dynamic_cast不適用於void *;

您可以嘗試將void *轉換爲某種基本類型,然後將dynamic_cast轉換爲任何想要的。但是,我不能保證將它與通過多重繼承創建的對象一起使用是安全的。使用一些抽象類來交換數據,而不是void *指針會更安全。

0

哪些其他C++方法可用來deepcopy的一個未知的數據類型(和運行過程中可能得到的數據類型信息不知何故人)

什麼你看這裏是序列化:放置的能力二進制流中的各種對象,將流移動 - 也許存儲它,然後從流中取回原始內容的副本。

你可以自己實現,或者你可以將你的實現基於現有的庫。

我使用的一個選項是boost :: serialization。您基本上必須在所有類層次結構中實現serialize方法,或在所有類層次結構中實現saveload方法。

代碼非常簡單。有關本教程,請參閱here

0

感謝您的快速解答。

我在某種程度上懶得在未來重寫代碼,每次添加新數據類型時,每個類都會有 。

對我來說,我現在可以使用模板爲每個數據類型使用, 但我真的從代碼開發的角度來看,如果有 不是在c + +中更簡單的事情。你是對的,我來自c和 我會慢慢地表現自己在C++編程。

我試圖用一個基類, 已經是編碼的限制繼承這種方法,因爲每個新數據類型 必須被編碼爲inherite基礎結構,但這是不行的, 因爲只有調用基礎結構的副本構造函數 「複製對象」知道的唯一東西(因爲它不知道關於派生結構datetypes的任何東西 )可以這樣做,不起作用。

派生結構的副本構造不叫,或者我在做 這裏有什麼錯?我試圖用賦值運算符,但 它不做一個副本,一旦原始數據被刪除, 它是一個懸掛指針我猜。

在它的工作原理是這樣

  1. 的「發送對象」的時刻創建一個結構DATA_TYPE1 *新DATA_TYPE1 ...
  2. 與數據
  3. 填充它且將其轉換到void *
  4. ,並告訴「複製對象」這個指針
  5. 那麼「發送對象」被刪除,無效*的內容還活着
  6. 「複製對象」專賣店只是虛空*
  7. 「接收對象」獲得這一空白*在以後的時間
  8. 且將其回結構DATA_TYPE1 *,並使用其數據
  9. 最後刪除該數據

所以沒有複製完成這裏只是將指針交給 原始數據從一個對象到另一個。

感謝關於序列化的意見,我想過, ,但此刻我想用純粹的C++來挑剔自己,以解決這個問題 。

+0

人們通常實現一個模仿虛擬拷貝構造函數的「克隆」方法,其中「clone」將簡單地返回新的T(* this);. – Puppy 2010-06-07 10:26:23