2013-07-03 85 views
1

我想在C/C++中創建一個通用鏈接列表(不使用C++模板)。 我已經寫了下面的程序,它工作正常,截至目前 -使用Void *指針的鏈接列表

typedef struct node 
{ 
    void *data; 
    node *next; 
}node; 


int main() 
{ 
    node *head = new node(); 

    int *intdata = new int(); 
    double *doubledata = new double(); 

    char *str = "a"; 
    *doubledata = 44.55; 
    *intdata = 10; 

    head->data = intdata; 

    node *node2 = new node(); 
    node2->data = doubledata; 
    head->next = node2; 

    node *node3 = new node(); 
    node3->data = str; 
    node3->next = NULL; 
    node2->next = node3; 

    node *temp = head; 
    if(temp != NULL) 
    { 
    cout<<*(int *)(temp->data)<<"\t"; 
    temp = temp->next; 
    } 
    if(temp != NULL) 
    { 
    cout<<*(double *)(temp->data)<<"\t"; 
    temp = temp->next; 
    } 
    if(temp != NULL) 
    { 
    cout<<*(char *)(temp->data)<<"\t"; 
    temp = temp->next; 
    } 
    return 0; 
} 

我的問題是 - 我需要知道我在上面的代碼打印數據的數據類型。例如 - 第一個節點是int,所以我寫了 - *(int *)(temp-> data) 第二個是double等等... 相反,是否有任何通用的方式來簡單顯示數據而不用擔心數據類型?

我知道你可以通過模板來實現這一點,但是如果我只需要在C中做到這一點呢?

感謝, 基達

+6

啊,C/C++,神奇的語言沒有可怕的模板和充分的就業機會...... –

+3

在C中'void *'的全部觀點是你*不知道類型。不知道類型,你不能明智地「顯示數據」。你甚至不知道要顯示多少數據。 –

+0

爲什麼不使用'boost :: any'? – rwols

回答

0

在C語言中,實現仿製藥的唯一方法是使用void*,因爲你已經在做。不幸的是,這意味着沒有簡單的方法來檢索鏈接列表元素的類型。你只需要知道他們。

+0

實際上,也有使用宏來實現具有特定類型的「節點」的可能性。 –

+0

但是,你必須爲每個你想放入鏈表的類型創建一個新的宏。 – Jashaszun

+0

不,宏可以將「類型」作爲參數... –

5

通用列表的要點是可以存儲任何內容。但你必須現實...你仍然需要知道什麼你正在把它。所以如果你打算把混合類型放在列表中,那麼你應該看看使用Variant模式。也就是說,提供多種類型的類型。這裏有一個簡單的變體:

typedef struct Variant 
{ 
    enum VariantType 
    { 
     t_string, 
     t_int, 
     t_double 
    } type; 

    union VariantData 
    { 
     char* strVal; 
     int  intVal; 
     double doubleVal; 
    } data; 

} Variant; 

然後你可以告訴自己「我存儲指針變種在我void*列表這是當你說你會怎麼做在C.我認爲‘C/C++’你的意思是你試圖編寫C代碼,但是使用C++編譯器,不要忘記C和C++是兩種不同的語言,它們有一些重疊,儘量不要把它們放在一個單詞中,就好像它們是一樣的語言

0

解釋內存中數據的方式對於不同的數據類型是完全不同的
假設一個32位內存塊有一些數據,當你將它轉換爲t或float,因爲兩者都以不同的協議存儲。當將一些數據保存在由void *類型的變量指向的內存中時,它不知道如何解釋其內存塊中的數據。所以你需要對它進行類型轉換來指定你想要讀取數據的類型。

0

這有點像把一個抽屜裏的所有餐具都粘在一起,但是不是把刀子放在一個槽裏,叉子放在另一個槽裏,在第三個槽裏放上匙子,在中間的小槽裏放上茶匙,我們只要把它們全部放在他們碰巧落地的任何地方,然後想知道爲什麼當你伸手去拿東西時,你不知道你會得到什麼。

C++的整點是它允許你聲明模板和類「處理任意內容」。由於上面的代碼使用new,因此它不會編譯爲C.因此,使其保存非描述性指針(甚至將數據作爲指針存儲在首位)沒有意義。

template<typename T> struct node 
{ 
    T data; 
    node<T> *next; 
    node() : next(0) {}; 
}; 

不幸的是,如果你想在同一個列表中存儲一組不同類型的數據,它仍然會變得更加複雜。如果你想這樣做,你需要在節點本身中指明你存儲的內容。

自從我在1985年開始使用計算機開始工作(可能還有幾次我找到工作之後),我已經在列表中做了幾次。更多次,我做了某種「我會存儲任意數據「,如std::map,其中名稱連接到某些」內容「。每次我使用這種功能時,都是因爲我正在編寫類似於編程語言的東西(例如配置腳本,基本解釋器,LisP解釋器等),使用它來存儲可能具有不同類型的「變量」 (int,double,string)或類似的。我在其他地方看過類似的東西,比如OpenGL有一些地方返回的數據是不同的類型,這取決於你要求的內容,而且內部存儲必須「知道」類型是什麼。

但是我所處理的所有鏈表,二叉樹,散列表等的99%都只包含一件事和一件事。將「任意」事物存儲在單個列表中通常沒有用處。

0

下面的答案是針對C++而不是C。C++允許你想要的東西,只是不以你想要的方式。我將實現您的問題的方式將使用虛擬關鍵字的內置功能。

下面是打印出不同的值,無論實際派生類型的獨立代碼示例:

#include <iostream> 
#include <list> 

class Base 
{ 
public: 

    virtual void Print() = 0; 
}; 

class Derived1 : public Base 
{ 
public: 

    virtual void Print() 
    { 
     std::cout << 1 << std::endl; // Integer 
    } 
}; 

class Derived2 : public Base 
{ 
public: 

    virtual void Print() 
    { 
     std::cout << 2.345 << std::endl; // Double 
    } 
}; 

class Derived3 : public Base 
{ 
public: 

    virtual void Print() 
    { 
     std::cout << "String" << std::endl; // String 
    } 
}; 

int main(void) 
{ 
    // Make a "generic list" by storing pointers to a base interface 
    std::list<Base*> GenericList; 
    GenericList.push_back(new Derived1()); 
    GenericList.push_back(new Derived2()); 
    GenericList.push_back(new Derived3()); 
    std::list<Base*>::iterator Iter = GenericList.begin(); 
    while(Iter != GenericList.end()) 
    { 
     (*Iter)->Print(); 
     ++Iter; 
    } 

    // Don't forget to delete the pointers allocated with new above. Omitted in example 

    return 0; 
} 

另請注意,這樣你就不需要實現自己的鏈表。標準列表在這裏工作得很好。但是,如果您仍想使用自己的列表,而不是存儲void *data;,請存儲Base *data;。當然,這可能是模板化的,但是你最終會再次選擇標準。

閱讀polymorphism瞭解更多信息。