void *の正当な使用法はありますか?


87

void*C ++での正当な使用法はありますか?それとも、Cが持っていたのでこれが導入されましたか?

私の考えを要約するだけです:

入力:複数の入力タイプを許可する場合は、関数とメソッドをオーバーロードできます。あるいは、共通の基本クラスまたはテンプレートを定義することもできます(回答でこれに言及していただきありがとうございます)。どちらの場合も、コードはより記述的でエラーが発生しにくくなります(基本クラスが適切な方法で実装されている場合)。

出力void*既知の基本クラスから派生したものとは対照的に、受信したい状況は考えられません 。

私が何を意味するのかを明確にするために:私はのユースケースがあるかどうかを具体的に尋ねているのではなく、最良の、または唯一の利用可能な選択肢void*がある場合があるかどうかを尋ねていvoid*ます。これは、以下の何人かの人々によって完全に答えられました。


3
intやstd :: stringのような複数の型が必要なときはどうですか?
アミール2015

12
@Amirは、variantany、組合のタグ付き。実際のコンテンツの種類を教えてくれ、より安全に使用できるものなら何でも。
Revolver_Ocelot 2015

15
「Chasit」は十分に強力な正当化であり、これ以上探す必要はありません。どちらの言語でも、それをできるだけ避けることは良いことです。
N。「代名詞」m。

1
ただ1つ、CスタイルのAPIとの相互運用は、それがないと扱いにくいということです。
マーティンジェームス

1
興味深い使用法は、ポインターのベクトルの型消去です
Paolo M

回答:


81

void*少なくとも::operator new(すべてのoperator new...)の結果mallocとして、および配置new演算子の引数として必要です。

void*すべてのポインタ型に共通のスーパータイプと考えることができます。したがって、それは正確にはへのポインタを意味するのではなくvoid、何かへのポインタを意味します。

ところで、いくつかの無関係なグローバル変数のデータを保持したい場合std::map<void*,int> score; は、global int x;and double y;and std::string s;do score[&x]=1;and score[&y]=2;andを宣言した後、いくつかのthenを使用できます。score[&z]=3;

memset 望んでいるvoid*アドレス(最も一般的なもの)

また、POSIXシステムにはdlsymがあり、その戻り値の型は明らかにvoid*


1
それはとても良い点です。実装側ではなく、言語ユーザーから考えていたことを忘れてしまいました。私が理解していないもう1つのこと:新しい関数はありますか?私はそれをキーワードとしてもっと考えました
magu_ 2015

9
newは、+やsizeofなどの演算子です。
ジョシュア

7
もちろん、メモリも返すことができますchar *。意味は少し異なりますが、彼らはこの側面に非常に近いです。
edmz 2015

@ヨシュア。それを知りませんでした。これを教えてくれてありがとう。これC++で書かれてC++いるので、持っているのに十分な理由void*です。このサイトが大好きです。1つの質問をして多くを学びましょう。
magu_ 2015

3
昔に戻る@black、Cはさえなかった持っているvoid*タイプを。使用されるすべての標準ライブラリ関数char*
Cole Johnson

28

使用する理由は複数ありますがvoid*、最も一般的な3つは次のとおりです。

  1. void*インターフェイスでを使用してCライブラリと対話する
  2. 型消去
  3. 型なしメモリを示す

逆の順序で、型指定されていないメモリを(またはバリアント)のvoid*代わりにchar*(3)で示すと、偶発的なポインタ演算を防ぐのに役立ちます。利用できる操作は非常に少ないvoid*ため、通常、役立つ前にキャストする必要があります。そしてもちろん、char*エイリアシングの問題はありません。

型消去(2)は、テンプレートと組み合わせて、またはそうでない場合でも、C ++で引き続き使用されます。

  • 非ジェネリックコードはバイナリの肥大化を減らすのに役立ち、ジェネリックコードでもコールドパスで役立ちます
  • 非ジェネリックコードは、次のようなジェネリックコンテナであっても、ストレージに必要な場合があります。 std::function

そして明らかに、あなたが扱うインターフェースがvoid*(1)を使用するとき、あなたはほとんど選択の余地がありません。


15

そうそう。C ++でも、テンプレート拡張からの余分なコードが重くなりすぎることがあるためでvoid *はなく、使用するtemplate<class T*>ことがあります。

通常、私はそれを型の実際の実装として使用し、テンプレート型はそれを継承してキャストをラップします。

また、カスタムスラブアロケーター(オペレーターの新しい実装)はを使用する必要がありますvoid *。これが、g ++void *がサイズ1であるかのようにポインタを算術的に許可する拡張機能を追加した理由の1つです。


ご回答ありがとうございます。確かに、テンプレートについて言及するのを忘れていました。しかし、パフォーマンスのペナルティが発生することはめったにないため、テンプレートはタスクにさらに適しているのではないでしょうか?(stackoverflow.com/questions/2442358/…
magu_ 2015

1
テンプレートはコードサイズのペナルティを導入しますが、これはほとんどの場合パフォーマンスのペナルティでもあります。
ジョシュア

struct wrapper_base {}; template<class T> struct wrapper : public wrapper_base {T val;} typedef wrapper* like_void_ptr;テンプレートを使用した最小限のvoid-*-simulatorです。
user253751 2015

3
@Joshua:「ほとんど」の引用が必要になります。
user541686 2015

@Mehrdad:L1キャッシュを参照してください。
ジョシュア

10

入力:複数の入力タイプを許可したい場合は、関数とメソッドをオーバーロードできます

本当。

あるいは、共通の基本クラスを定義することもできます。

これは部分的に真実です。共通の基本クラスやインターフェースなどを定義できない場合はどうでしょうか。それらを定義するには、ソースコードにアクセスする必要がありますが、これは多くの場合不可能です。

テンプレートについては触れていません。ただし、テンプレートはポリモーフィズムに役立ちません。つまり、コンパイル時に既知の静的型で機能します。

void*最小公分母と見なすことができます。C ++では、(i)本質的に多くのことを行うことができず、(ii)ほとんどの場合、より優れたソリューションがあるため、通常必要ありません

さらに、通常は他の具体的なタイプに変換することになります。そのためchar *、通常は優れていますが、純粋なデータブロックではなく、Cスタイルの文字列を期待していることを示している場合があります。他のポインタタイプからの暗黙的なキャストが可能になるため、それvoid*よりも優れているのはそのchar*ためです。

あなたはいくつかのデータを受け取り、それを操作して出力を生成することになっています。これを実現するには、使用しているデータを知る必要があります。そうしないと、最初に解決していたものとは異なる別の問題が発生します。void*たとえば、多くの言語には問題がなく、問題もありません。

別の合法的な使用

ポインタのような関数でポインタアドレスを出力する場合printfvoid*型が必要であるため、void*へのキャストが必要になる場合があります。


7

はい、それはその言語の他のものと同じくらい便利です。
例として、最小限で柔軟なインターフェイスを実現するために、必要に応じて静的に適切な型にキャストできるクラスの型を消去するために使用できます。

ではその応答あなたのアイデアを与える必要があります使用例があります。
わかりやすくするために、以下にコピーして貼り付けます。

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;
};

明らかに、これはの使用法の1つにすぎませんvoid*


4

ポインタを返す外部ライブラリ関数とのインターフェース。これはAdaアプリケーション用のものです。

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

void* m_status_ptr = ada_function();

これは、エイダがあなたに伝えたかったことへのポインタを返します。あなたはそれで特別なことをする必要はありません、あなたは次のことをするためにそれをエイダに返すことができます。実際、C ++でAdaポインターを解きほぐすことは簡単ではありません。


autoこの場合、代わりに使用できませんでしたか?コンパイル時に型がわかっていると仮定します。
magu_ 2015

ああ、今日はおそらくできるでしょう。そのコードは、私がいくつかのAdaラッパーを作成した数年前のものです。
RedSonja 2015

エイダのタイプは悪魔のようなものになる可能性があります-彼らはそれをトリッキーにすることで彼ら自身を作り、そしてひねくれた喜びを取ります。私はインターフェースを変更することを許可されていませんでした。それは簡単すぎたでしょう、そしてそれはそれらのvoidポインターに隠されたいくつかの厄介なものを返しました。うーん。
RedSonja 2015

2

要するに、厳密な言語としてのC ++(malloc()のようなCの遺物を考慮しない)は、すべての可能なタイプの共通の親を持たないため、void *を必要とします。たとえば、オブジェクトを持つObjCとは異なります


mallocそして、newリターンの両方をvoid *オブジェクトクラスは、C ++にあった場合でも場合は、必要がありますので、
ドミトリーGrigoryev

mallocは
遺棄されてい

では、整数の配列をどのように割り当てますか?
Dmitry Grigoryev 2015

@DmitryGrigoryevoperator new()はを返しますvoid *が、new式は返しません
MM

1.私は確信して私が見てみたいと思いませんよ仮想すべてのクラス以上の基底クラスのオブジェクトとタイプを含め、intそれは非仮想EBCだ場合2.、そしてそれはどのように異なっていますかvoid*
lorro 2016

1

私が最初に思い浮かぶのは(上記のいくつかの答えの具体的なケースだと思います)、Windowsのthreadprocにオブジェクトインスタンスを渡す機能です。

これを行う必要のあるC ++クラスがいくつかあります。それらにはワーカースレッドの実装があり、CreateThread()APIのLPVOIDパラメーターはクラスの静的メソッド実装のアドレスを取得するため、ワーカースレッドは次の処理を実行できます。クラスの特定のインスタンス。threadprocでの単純な静的キャストバックにより、インスタンスが機能するようになり、インスタンス化された各オブジェクトが単一の静的メソッド実装からのワーカースレッドを持つことができます。


0

多重継承の場合、オブジェクトが占めるメモリチャンクの最初のバイトへのポインタを取得する必要がある場合はdynamic_castvoid*

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.