Void * 有合法用途吗?

在 C + + 中 void*有合法的使用吗? 或者这是因为 C 拥有它而引入的?

我想重述一下我的想法:

Input : 如果我们想要允许多种输入类型,我们可以重载函数和方法,或者我们可以定义一个通用的基类或模板(感谢您在答案中提到这一点)。在这两种情况下,代码都具有更好的描述性和更少的错误倾向(前提是基类是以一种理智的方式实现的)。

输出 : 我想不出任何情况下我更愿意接收 void*,而不是从已知的基类派生的内容。

只是为了弄清楚我的意思: 我并不是特别地询问是否有 void*的用例,而是询问是否有 void*是最佳或唯一可用的选择的情况。下面的几个人已经完美地回答了这个问题。

5620 次浏览

void* is at least necessary as the result of ::operator new (also every operator new...) and of malloc and as the argument of the placement new operator.

void* can be thought as the common supertype of every pointer type. So it is not exactly meaning pointer to void, but pointer to anything.

BTW, if you wanted to keep some data for several unrelated global variables, you might use some std::map<void*,int> score; then, after having declared global int x; and double y; and std::string s; do score[&x]=1; and score[&y]=2; and score[&z]=3;

memset wants a void* address (the most generic ones)

Also, POSIX systems have dlsym and its return type evidently should be void*

Oh yes. Even in C++ sometimes we go with void * rather than template<class T*> because sometimes the extra code from the template expansion weighs too much.

Commonly I would use it as the actual implementation of the type, and the template type would inherit from it and wrap the casts.

Also, custom slab allocators (operator new implementations) must use void *. This is one of the reasons why g++ added an extension of permitting pointer arithmatic on void * as though it were of size 1.

Input: If we want to allow multiple input types we can overload functions and methods

True.

alternatively we can define a common base class.

This is partially true: what if you can't define a common base class, an interface or similar? To define those you need to have access to the source code, which is often not possible.

You didn't mention templates. However, templates cannot help you with polymorphism: they work with static types i.e. known at compile time.

void* may be consider as the lowest common denominator. In C++, you typically don't need it because (i) you can't inherently do much with it and (ii) there are almost always better solutions.

Even further, you will typically end up on converting it to other concrete types. That's why char * is usually better, although it may indicate that you're expecting a C-style string, rather than a pure block of data. That's whyvoid* is better than char* for that, because it allows implicit cast from other pointer types.

You're supposed to receive some data, work with it and produce an output; to achieve that, you need to know the data you're working with, otherwise you have a different problem which is not the one you were originally solving. Many languages don't have void* and have no problem with that, for instance.

Another legitimate use

When printing pointer addresses with functions like printf the pointer shall have void* type and, therefore, you may need a cast to void*

Yes, it is as useful as any other thing in the language.
As an example, you can use it to erase the type of a class that you are able to statically cast to the right type when needed, in order to have a minimal and flexible interface.

In that response there is an example of use that should give you an idea.
I copy and paste it below for the sake of clarity:

class Dispatcher {
Dispatcher() { }


template<class C, void(C::*M)() = C::receive>
static void invoke(void *instance) {
(static_cast<C*>(instance)->*M)();
}


public:
template<class C, void(C::*M)() = &C::receive>
static Dispatcher create(C *instance) {
Dispatcher d;
d.fn = &invoke<C, M>;
d.instance = instance;
return d;
}


void operator()() {
(fn)(instance);
}


private:
using Fn = void(*)(void *);
Fn fn;
void *instance;
};

Obviously, this is only one of the bunch of uses of void*.

There are multiple reasons to use void*, the 3 most common being:

  1. interacting with a C library using void* in its interface
  2. type-erasure
  3. denoting un-typed memory

In reverse order, denoting un-typed memory with void* (3) instead of char* (or variants) helps preventing accidental pointer arithmetic; there are very few operations available on void* so it usually require casting before being useful. And of course, much like with char* there is no issue with aliasing.

Type-erasure (2) is still used in C++, in conjunction with templates or not:

  • non-generic code helps reducing binary bloat, it's useful in cold paths even in generic code
  • non-generic code is necessary for storage sometimes, even in generic container such as std::function

And obviously, when the interface you deal with uses void* (1), you have little choice.

In short, C++ as a strict language (not taking into account C relics like malloc()) requires void* since it has no common parent of all possible types. Unlike ObjC, for example, which has object.

Interfacing with an external library function which returns a pointer. Here is one for an Ada application.

extern "C" { void* ada_function();}


void* m_status_ptr = ada_function();

This returns a pointer to whatever it was Ada wanted to tell you about. You don't have to do anything fancy with it, you can give it back to Ada to do the next thing. In fact disentangling an Ada pointer in C++ is non-trivial.

The first thing that occurs to my mind (which I suspect is a concrete case of a couple of the answers above) is the capability to pass an object instance to a threadproc in Windows.

I've got a couple of C++ classes which need to do this, they have worker thread implementations and the LPVOID parameter in the CreateThread() API gets an address of a static method implementation in the class so the worker thread can do the work with a specific instance of the class. Simple static cast back in the threadproc yields the instance to work with, allowing each instantiated object to have a worker thread from a single static method implementation.

In case of multiple inheritance, if you need to get a pointer to the first byte of a memory chunk occupied by an object, you may dynamic_cast to void*.