2013-07-03 1 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) second double и т. Д. Вместо этого существует ли какой-либо общий способ простого отображения данных, не беспокоясь о тип данных?

Я знаю, что вы можете достичь этого с помощью шаблонов, но что делать, если я должен делать это только на C?

Спасибо, Кедар

+6

Ahh, C/C++, этот волшебный язык без страшных шаблонов и возможностей для трудоустройства ... –

+3

Весь смысл 'void *' в C состоит в том, что вы * не знаете тип. Не зная тип, вы не можете разумно «отображать данные». Вы даже не знаете, сколько * данных для отображения. –

+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, где имя связано с каким-то« контентом ». Каждый раз, когда я использовал эту функцию, это потому, что я пишу что-то похожее на язык программирования (например, сценарий конфигурации, интерпретатор Basic, интерпретатор LisP и т. Д.), Используя его для хранения «переменных», которые могут иметь разные типы (int, double, string) или аналогичный. Я видел подобные вещи в других местах, например, OpenGL имеет некоторые места, где возвращаемые данные являются разными типами в зависимости от того, что вы просите, а внутреннее хранилище должно «знать», что такое тип.

Но 99% всех связанных списков, двоичных деревьев, хеш-таблиц и т. Д., Над которыми я работал, содержат только одну вещь и одну вещь. Хранение «произвольных» вещей в одном списке обычно не так полезно.

0

Нижеприведенный ответ ориентирован на C++, а не C. C++ разрешает то, что вы хотите, просто не так, как вы хотите это сделать. То, как я буду реализовывать вашу проблему, будет использовать встроенную функциональность ключевого слова virtual.

Вот автономный пример кода, который выводит различные значения независимо от фактического производного типа:

#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, чтобы узнать больше.

 Смежные вопросы

  • Нет связанных вопросов^_^