2017-09-27 7 views
0

についての詳細については、スタックを混乱させてください、The C Programming Language (K&R)The C++ Programming Language (Stroustrup)のような本。私は何を学んだのですか?そのvoid*は、推論された型のない汎用ポインタです。任意の定義されたタイプへのキャストが必要で、void*の印刷だけでアドレスが得られます。もう一つのvoid * topic;私は混乱しているので尋ねる必要があります

他に何を知っていますか? void*は逆参照することはできません。これまでのところ、多くの書面が見つかりましたが、ほとんど理解できなかったことが発見されたC/C++の項目の1つです。

*(char*)void*のようにキャストする必要があることを理解していますが、genericポインタの意味がわからないということは、値を取得するためにどのようなタイプが必要なのかということです。私はJavaプログラマです。私はジェネリック型を理解していますが、これは私が苦労しているものです。

だから、私は、後にメモリ解放を処理しますいくつかのコード

typedef struct node 
{ 
    void* data; 
    node* link; 
}Node; 

typedef struct list 
{ 
    Node* head; 
}List; 

Node* add_new(void* data, Node* link); 

void show(Node* head); 

Node* add_new(void* data, Node* link) 
{ 
    Node* newNode = new Node(); 
    newNode->data = data; 
    newNode->link = link; 

    return newNode; 
} 

void show(Node* head) 
{ 
    while (head != nullptr) 
    { 
     std::cout << head->data; 
     head = head->link; 
    } 
} 

int main() 
{ 
    List list; 

    list.head = nullptr; 

    list.head = add_new("My Name", list.head); 

    list.head = add_new("Your Name", list.head); 

    list.head = add_new("Our Name", list.head); 

    show(list.head); 

    fgetc(stdin); 

    return 0; 
} 

を書きました。 void*に格納されている型がわからないと仮定して、どのように値を取得するのですか? Thisは既にタイプを知る必要があることを暗示していますが、は、void*という一般的な性質については何も明らかにしていませんが、まだ理解していませんが、hereです。

なぜ私はvoid*が協力して、ヒープやスタックの一部のレジスタに内部的に隠されている型を自動的にキャストすることを期待していますか?

+1

私は 'void *'の使用は次のようだと考えています:WindowのCWndクラスは 'void * data'を保持できます。このデータが何であるかを知りませんし、気にもなりません。この「データ」は何もしません。単にそれを保持しています。一方、私のコードは私の 'thingamabob'をこの' data'に格納することができます。私のコードは、 'data'は常に' thingamabob'を保持していると仮定しています。これは、 'data'メンバにアクセスするコードが自分のコードだけであるからです。私のコードは型を知っていますが、 'CWnd'は型を知っていません。 –

答えて

5

後でメモリの割り当て解除を処理します。私がvoid *に格納されている型を理解していないと仮定すると、値をどのように取り出すのですか?

できません。 は、逆参照する前にポインタをキャストできる有効な型を認識している必要があります。あなたがC++ 17コンパイラを使用することができるならば、あなたはstd::anyを使用することができ

  1. :ここ

    は、ジェネリック型を使用するためのオプションのカップルです。

  2. ブーストライブラリを使用できる場合は、boost::anyを使用できます。
+0

cまたはC++で ''純粋な ''ジェネリックスを実行する方法はありませんか? – Mushy

+2

@Mushy、いいえ、コア言語ではありません。 ['std :: any'](http://en.cppreference.com/w/cpp/utility/any)と[' boost :: any'](http(http://www.cppreference.com/w/cpp/utility/any)の形式でジェネリックオブジェクト型を作る試みがあります。 ://www.boost.org/doc/libs/1_61_0/doc/html/any.html) –

+1

@RSahu C++に 'std :: any'があります。17 – Swift

1

Javaとは異なり、C/C++ではメモリポインタを使用して作業しています。カプセル化はまったくありません。 void *タイプは変数がメモリ内のアドレスであることを意味します。何でもそこに保存することができます。 int *のようなタイプでは、あなたが何を参照しているのかをコンパイラに伝えます。コンパイラは、型のサイズ(例えば、intの場合は4バイト)を知っています。その場合、アドレスは4の倍数(粒度/メモリの整列)になります。一番上に、コンパイラに型を渡すと、コンパイル時に一貫性チェックが実行されます。後でない。これはvoid *では起こりません。

簡単に言えば、あなたはベアメタルで作業しています。型はコンパイラ指令であり、ランタイム情報を保持しません。また、動的に作成しているオブジェクトも追跡されません。これは、最終的に何かを格納できる場所に割り当てられるメモリ内の単なるセグメントです。

0

void *を使用する主な理由は、異なることが指摘される可能性があることです。したがって、私はint *またはNode *または何か他のものを渡すかもしれません。しかし、あなたがタイプまたは長さのいずれかを知っていない限り、あなたはそれで何もできません。

しかし、長さを知っていれば、タイプを知らなくても、指しているメモリを扱うことができます。私はvoid *とバイト数を持っているので、私は別の場所にメモリをコピーするか、ゼロを出力することができるので、char *としてキャストするのは1バイトなので使用されます。

さらに、それがクラスへのポインタであるにもかかわらず親クラスまたは継承クラスであるかどうかわからない場合は、そのクラスを想定して、 。しかし、何をしても、別の関数に渡す以外に多くのことをしたいときは、それを何かにキャストする必要があります。 char *は使用する最も簡単な1バイト値です。

0

あなたの混乱は、Javaプログラムに対処する習慣から派生したものです。 Javaコードは仮想マシンのための命令セットであり、RAMの機能は各オブジェクトの名前、タイプ、サイズおよびデータを格納する一種のデータベースに与えられる。今学習しているプログラミング言語は、基礎となるOSと同じメモリ構成で、CPUのための命令にコンパイルされます。 CおよびC++言語で使用される既存のモデルは、そのプラットフォームおよびOS用にコンパイルされた後にコードが効果的に機能するように、ほとんどの一般的なOSの上に構築された抽象です。当然のことながら、C++の有名なRTTIを除いて、タイプに関する文字列データは含まれていません。

ケースの場合RTTIは、データを格納する裸ポインタの周りにラッパーを作成しない限り、直接使用することはできません。

実際、C++ライブラリには、ISO標準で定義されている場合、使いやすくポータブルな膨大な数のコンテナクラステンプレートが含まれています。標準の3/4は、しばしばSTLと呼ばれるライブラリの説明です。何らかの理由で自分のコンテナを作成しない限り、裸のポインタを使用するよりも、それらの使用が望ましいです。特定のタスクでは、C++ 17標準では、以前にブーストライブラリに存在していたstd::anyクラスが提供されていました。もちろん、再実装することも、場合によってはstd::variantで置き換えることもできます。

0

私はあなたがいない

を値を得るのですか、私はボイド*に保存されているタイプの全く理解していないと仮定。

あなたができることは、void*に保存されているタイプを記録することです。

void*は、1つの抽象レイヤーをポイントするデータのバイナリチャンクを渡すために使用され、コードを渡すことがわかっているタイプにキャストして戻します。ここ

void do_callback(void(*pfun)(void*), void* pdata) { 
    pfun(pdata); 
} 

void print_int(void* pint) { 
    printf("%d", *(int*)pint); 
} 

int main() { 
    int x = 7; 
    do_callback(print_int, &x); 
} 

は、我々は&xのthet YPEを忘れて、do_callbackを通してそれを渡します。

後でvoid*が実際にint*であることを知っていることをコードdo_callback内部または他の場所に渡されます。それで、それは戻ってそれをキャストし、intとしてそれを使用します。

void*と消費者void(*)(void*)が結合されています。上記のコードは "確かに正しい"ものですが、その証明は型システムにはありません。その代わりに、それがint*であることを知っている文脈では、void*しか使用しないという事実に依存する。


void*も同様に使用できます。しかし、あなたはまた好奇心を得ることができます。

ポインタを印刷可能なものにしたいとします。 <<std::ostreamの場合は何かが印刷可能です。私は何をしたか

struct printable { 
    void const* ptr = 0; 
    void(*print_f)(std::ostream&, void const*) = 0; 

    printable() {} 
    printable(printable&&)=default; 
    printable(printable const&)=default; 
    printable& operator=(printable&&)=default; 
    printable& operator=(printable const&)=default; 

    template<class T,std::size_t N> 
    printable(T(&t)[N]): 
    ptr(t), 
    print_f([](std::ostream& os, void const* pt) { 
     T* ptr = (T*)pt; 
     for (std::size_t i = 0; i < N; ++i) 
     os << ptr[i]; 
    }) 
    {} 
    template<std::size_t N> 
    printable(char(&t)[N]): 
    ptr(t), 
    print_f([](std::ostream& os, void const* pt) { 
     os << (char const*)pt; 
    }) 
    {} 
    template<class T, 
    std::enable_if_t<!std::is_same<std::decay_t<T>, printable>{}, int> =0 
    > 
    printable(T&& t): 
    ptr(std::addressof(t)), 
    print_f([](std::ostream& os, void const* pt) { 
     os << *(std::remove_reference_t<T>*)pt; 
    }) 
    {} 
    friend 
    std::ostream& operator<<(std::ostream& os, printable self) { 
    self.print_f(os, self.ptr); 
    return os; 
    } 
    explicit operator bool()const{ return print_f; } 
}; 

(Java型消去に漠然と似た)C++での「型消去」と呼ばれる技術です。

void send_to_log(printable p) { 
    std::cerr << p; 
} 

Live example

ここでは、タイプ上に印刷するという概念にアドホックな「仮想」インターフェースを作成しました。

タイプは実際のインターフェイス(バイナリレイアウト要件なし)をサポートする必要はなく、特定の構文をサポートするだけでよい。

任意のタイプの独自の仮想ディスパッチテーブルシステムを作成します。

これはC++標準ライブラリで使用されます。 にはstd::function<Signature>があり、にはstd::anyがあります。

std::anyvoid*です。その内容を破棄してコピーする方法を知っています。タイプを知っていれば元のタイプに戻すことができます。それを照会して特定のタイプであるかどうか尋ねることもできます。

std::anyを上記のタイプ消去テクニークと組み合わせることで、任意のダックタイプのインターフェイスを持つ通常のタイプ(値は参照ではなく振る舞います)を作成できます。

関連する問題