1

我想在 C/C++ 中创建一个通用链表(不使用 C++ 模板)。我已经编写了以下简单程序,到目前为止它工作正常 -

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 中这样做呢?

谢谢, 凯达

4

5 回答 5

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*列表中存储指向 Variants 的指针。这就是您在 C 中的做法。我假设当您说“C/C++”时,您的意思是您正在尝试编写 C 代码,但是使用 C++ 编译器。不要忘记 C 和 C++ 是两种不同的语言,它们有一些重叠。尽量不要把它们放在一个词中,就好像它们是一种语言一样。

于 2013-07-03T22:47:48.867 回答
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;
}

另请注意,这样您就不需要实现自己的链表。标准列表在这里工作得很好。但是,如果您仍想使用自己的列表,而不是存储 a void *data;,存储 a Base *data;。当然,这可以被模板化,但是你最终会再次使用标准。

阅读多态性以了解更多信息。

于 2013-07-03T23:21:33.410 回答
0

对于不同的数据类型,解释内存中数据的方式是完全不同的。
假设一个 32 位内存块有一些数据。当您将其类型转换为 int 或 float 时,它将显示不同的值,因为两者都使用不同的协议存储。当将一些数据保存在 void* 类型变量指向的内存中时,它不知道如何解释其内存块中的数据。因此,您需要对其进行类型转换以指定要读取数据的类型。

于 2013-07-03T22:48:17.053 回答
0

在 C 中,实现泛型的唯一方法是使用 a void*,正如您已经在做的那样。不幸的是,这意味着没有简单的方法来检索链表元素的类型。您只需要了解它们。

于 2013-07-03T22:38:21.647 回答
0

这有点像把所有的餐具都放在抽屉里,但不是把刀放在一个槽里,叉子放在另一个槽里,勺子放在第三个槽里,茶匙放在中间的小槽里,我们只是把它们都粘起来当他们把它们扔进去时,它们碰巧落在哪里,然后想知道为什么当你把手伸进去捡东西时,你不知道你会得到什么。

C++ 的全部意义在于它允许您声明“使用任意内容执行操作”的模板和类。由于上面的代码使用new,它不会编译为 C。所以没有必要让它保存一个非描述性的指针(或者甚至首先将数据存储为指针)。

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

不幸的是,如果您想在同一个列表中存储一组不同类型的数据,它仍然会变得更加混乱。如果你想这样做,你需要在节点本身中有一些东西来表明你存储了什么。

自从我 1985 年开始在计算机上工作(可能在我找到工作之前的几次)以来,我已经在列表中做过几次。更多次,我做了某种“我将存储任意数据”在 a 之类的东西std::map中,名称与某些“内容”相关联。每次我使用这种功能时,都是因为我正在编写类似于编程语言的东西(例如配置脚本、Basic 解释器、LisP 解释器等),用它来存储可以具有不同类型的“变量” ( int, double, string) 或类似的。我在其他地方也看到过类似的事情,比如 OpenGL 有一些地方根据你的要求返回的数据是不同的类型,并且内部存储必须“知道”类型是什么。

但是我研究过的所有链表、二叉树、哈希表等中的 99% 只包含一件事和一件事。在一个列表中存储“任意”的东西通常没有那么有用。

于 2013-07-03T22:57:39.150 回答