何时使用void指针?

Mac*_*c13 42 c c++ pointers

我理解使用void指针来实现malloc.

void* malloc  ( size_t size );
Run Code Online (Sandbox Code Playgroud)

任何人都可以提出其他原因或提供一些在实践中有用的场景.

谢谢

Art*_*ger 30

一个好的场景void*使用是当你想要实现任何通用的ADT时,如果你只是不知道它将保留和处理什么数据类型.例如,链接列表如下所示:

typedef struct node_t node;
struct
{
    void* data;
    node* prev, next;
} node_t;

typedef struct list_t list;
typedef void* (func)(void*) cpy_func;
typedef void (func)(void*) del_func;
struct
{
   node* head, tail, curr;
   cpy_func copy;
   del_func delete;
} list_t;

initializeLinkedList(cpy_func cpy, del_func del);
//here you keep going defining an API
Run Code Online (Sandbox Code Playgroud)

例如,您将初始化函数指针传递给其他函数,这些函数将能够将您的数据类型复制到列表中并在之后释放它.因此,通过使用void*您使您的列表更通用.

我认为void*只有因为向后兼容才能保留在C++中,因为在C++中你有更安全和复杂的方法来实现相同的结果,比如模板,仿函数等,而你在编写C++时不需要使用malloc.

关于C++,我没有任何具体的有用示例.

  • 使用`void*`而不是`char*`提供了一种特定的类型安全措施.你告诉编译器"我永远不应该允许取消引用其中一个指针." (3认同)
  • 谢谢 Artem。就是这样。同样在 C 语言中,同样的事情可以使用 char* 来实现。我无法想到不能使用 char* 代替的场景。(适用于 malloc )。 (2认同)

Kek*_*koa 14

如果您正在与C代码接口并需要通过C++对象,但C库只接受通用指针,那么当您检索指针时,需要将其重新转换为正确的类型.

可能不应经常使用Void指针,但是当您尝试使用适用于任意指针的库函数时,它们可以提供帮助,而且并不真正关心该内存表示哪些数据.


Ger*_*ard 9

void只要数据块的内容不重要,就应该使用指针.例如,在复制数据时,复制存储区的内容,但数据的格式并不重要.

对于在内存块上运行而不需要使用void指针理解内容的函数,可以向用户说明设计,以便他们知道函数不关心任何数据格式.char *当函数实际上是内容不可知时,通常编码的函数用于处理内存块.


Jos*_*osh 9

在C++中,我发现void*指针最引人注目的用例是为代码提供在他们已经使用的对象上存储任意"用户数据"的选项.

假设您已经编写了一个代表a的类Car,用于在软件中使用Car对象(流量模拟,租车清单等等).现在让我们假设你发现自己处于一种情况,你的应用程序想要跟踪一个主干的任意内容Car.存储在主干中的细节对于Car类来说并不重要,可能是任何东西 - 它实际上取决于使用Car类的应用程序的目的.输入void*指针.

class Car
{
    public:

        // Existing methods of your Car class

        void setContentsOfTrunk(void* contentsOfTrunk);
        void* contentsOfTrunk() const;

    private:

        void* m_contentsOfTrunk;
}
Run Code Online (Sandbox Code Playgroud)

现在,使用您的Car类的任何应用程序都可以选择将任意数据对象附加到现有Car对象,以便可以从具有该Car对象的任何代码中获取它.Car无论在代码中的哪个位置,主干的内容都"与对象一起旅行" .

在这种情况下,使用void*有两种选择.

第一种是根据trunk内容对象的类型来模板化你的类:

template <class TrunkContentsType>
class Car
{
    public:

        // Existing methods of your Car class

        void setContentsOfTrunk(TrunkContentsType contentsOfTrunk);
        TrunkContentsType contentsOfTrunk() const;

    private:

        TrunkContentsType m_contentsOfTrunk;
}
Run Code Online (Sandbox Code Playgroud)

这似乎是不必要的侵入性.中继的内容类型仅对应用程序很重要.使用Car对象的算法和数据结构并不关心主干中的内容.通过模板化类,您强制使用类的应用程序为中继内容选择类型,但在许多情况下,应用程序也不关心中继内容.

第二种方法是从Car派生一个新类,它为trunk内容添加一个数据成员和访问器:

class Car
{
    public:

        // Existing methods of your Car class
        // No methods having anything to do with trunk contents.

    private:

        // No data member representing trunk contents.
}

class CarWithTrunkContents
{
    public:

        // Existing methods of your Car class

        void setContentsOfTrunk(TrunkContentsType contentsOfTrunk);
        TrunkContentsType contentsOfTrunk() const;

    private:

        TrunkContentsType m_contentsOfTrunk;
}
Run Code Online (Sandbox Code Playgroud)

CarWithTrunkContents类是一个特定于应用程序的类,它添加了应用程序在汽车上存储中继内容所需类型的数据成员.这似乎也是不必要的重量级.为什么必须派生一个全新的类来添加一个不影响类行为的额外数据?如果使用Car该类的应用程序想要存储中继内容是相当常见的,为什么强制每个应用程序为其特定类型的中继内容派生一个新类?

最后,虽然我设计的主干内容示例可能描绘了与Car对象一起旅行的任意主干内容的生动画面,但实际上您可能会提供一种更通用的机制来将特定于应用程序的数据附加到Car:

class Car
{
    public:

        // Existing methods of your Car class

        void setUserData(void* userData);
        void* userData() const;

    private:

        void* m_userData;
}
Run Code Online (Sandbox Code Playgroud)

这样,应用程序可以附加表示主干内容的对象,或表示驾驶执照和注册的对象,或表示租赁协议的对象,或其他任何内容.我已经看到这种void*指针被称为"userData"(即由类的用户理解),"blindData"(即该类对其携带的对象的内容是盲目的)或"applicationData"(即由申请定义的类型和目的的数据.