7

私は主に K&R で C を学んでいますが、今ではオブジェクト指向 C pdf チュートリアルを見つけて、魅了されています。私はそれを行っていますが、私のCスキル/知識はそのタスクに対応していない可能性があります. これはチュートリアルです: http://www.planetpdf.com/codecuts/pdfs/ooc.pdf

私の質問は、pdf の最初の数章でさまざまな機能を調べたところから来ています。以下はそのうちの1つです。(pdfの14ページ)

void delete(void * self){
     const struct Class ** cp = self;

     if (self&&*cp&&(*cp)->dtor)
                self = (*cp)->dtor(self);
     free(self);

 }

dtor はデストラクタ関数ポインタです。しかし、これに関する知識は私の質問には必要ありません。

  • 私の最初の質問は、なぜ **cp が一定なのですか? コード作成者が誤って損害を与えないようにするために、それは必要ですか、それとも単に徹底しているだけですか?
  • 第二に、なぜ cp はポインターからポインターへのポインターなのですか (二重アスタリスク?)。struct クラスは、pdf の 12 ページで定義されています。自己ポインターをクラスポインターにキャストしているため、単一のポインターにできない理由がわかりません。
  • 第三に、void ポインターはどのようにクラス ポインター (またはクラス ポインターへのポインター) に変更されますか? この質問は、私の C の理解不足を最もよく表していると思います。私の頭の中で想像しているのは、一定量のメモリを占有する void ポインターですが、クラスには多くの「もの」があるため、それはクラス ポインターよりも少なくなければなりません。初期化。void ポインターを別のタイプのポインターに「キャスト」できることは知っていますが、これを実行するのに十分なメモリがない可能性があるため、その方法がわかりません。

前もって感謝します

4

3 に答える 3

3

興味深いpdf。

私の最初の質問は、なぜ**cpが一定なのかということです。コードライターが偶然に損害を与えるようなことをしないようにする必要があるのでしょうか、それとも徹底しているだけでしょうか。

ライターが誤って何もしないようにする必要があります。そうです。ポインターの性質とその使用法について、コードのリーダーに何かを伝える必要があります。

次に、cpがポインタからポインタへのポインタであるのはなぜですか(二重アスタリスク?)。構造体クラスは、PDFの12ページで定義されています。自己ポインタをクラスポインタにキャストしているので、なぜそれが単一のポインタになれないのかわかりません。

new()ポインタが作成される場所(13ページ)の定義を見てくださいp(と同じポインタが渡されますselfdelete()

void * new (const void * _class, ...)
{
    const struct Class * class = _class;
    void * p = calloc(1, class —> size);
    * (const struct Class **) p = class;

したがって、「p」にはスペースが割り当てられ、逆参照されてポインタ値が割り当てられます(クラス内のアドレス。これは、intポインタの逆参照と割り当てに似ていますが、intの代わりにアドレスを割り当てます)。 これは、pの最初のものがそのクラス定義へのポインタであることを意味します。 ただし、pにはそれ以上のスペースが割り当てられました(オブジェクトのインスタンスデータも保持されます)。delete()ここでもう一度考えてみましょう。

 const struct Class ** cp = self;
 if (self&&*cp&&(*cp)->dtor)

cpが逆参照されると、それはポインターへのポインターであったため、現在はポインターになっています。ポインタには何が含まれていますか?アドレス。住所は? pが指すブロックの先頭にあるクラス定義へのポインター。

pは実際にはポインタへのポインタではないため、これは一種の賢い方法です。特定のオブジェクトデータを含むより大きなメモリチャンクが割り当てられています。ただし、そのブロックの最初にはアドレス(クラス定義のアドレス)があるため、pが(キャストまたはcpを介して)ポインターに逆参照されると、その定義にアクセスできます。したがって、クラス定義は1つの場所にのみ存在しますが、そのクラスの各インスタンスには定義への参照が含まれています。わかる?pが次のような構造体として入力された場合はより明確になります。

struct object {
  struct class *class;
    [...]
};

p->class->dtor()次に、の既存のコードの代わりに次のようなものを使用できますdelete()。ただし、これは混乱を招き、全体像を複雑にします。

第三に、voidポインターはどのようにしてクラスポインター(またはクラスポインターへのポインター)に変更されますか?この質問は、Cについての私の理解の欠如を最もよく示していると思います。私の頭の中で想像するのは、一定量のメモリを消費するvoidポインタですが、クラスには多くの「もの」があるため、クラスポインタよりも少なくする必要があります。初期化。

ポインタはintのようなもので、値を保持するための小さな設定サイズがあります。その値はメモリアドレスです。*(またはを介して)ポインタを逆参照する場合->、アクセスしているのはそのアドレスのメモリです。ただし、メモリアドレスはすべて同じ長さであるため(たとえば、64ビットシステムでは8バイト)、ポインタ自体はタイプに関係なくすべて同じサイズです。これは、オブジェクトポインタ「p」の魔法がどのように機能したかです。繰り返しになりますが、メモリブロックの最初のpポイントはアドレスです。これにより、ポインタとして機能できるようになります。これが逆参照されると、クラス定義を含むメモリブロックが取得されます。のインスタンスデータとは別にp

于 2012-05-03T18:33:49.880 に答える
2
  1. この場合、それは単なる予防策です。関数はクラスを変更するべきではありません (実際には、おそらく何もすべきではありません)。そのため、にキャストするconst struct Class *と、クラスが誤って変更されにくくなります。

  2. 私は、ここで使用されているオブジェクト指向の C ライブラリにあまり詳しくありませんが、これは厄介なトリックだと思います。の最初のポインターselfはおそらくクラスへの参照であるため、逆参照selfするとクラスへのポインターが得られます。実際にselfは、常に として扱うことができますstruct Class **

    ここで図が役立つ場合があります。

            +--------+
    self -> | *class | -> [Class]
            |  ....  |
            |  ....  |
            +--------+
    
  3. すべてのポインターは単なるアドレスであることを忘れないでください。* ポインターの型は、ポインターのサイズには影響しません。システムに応じて、すべて 32 ビットまたは 64 ビット幅なので、いつでも 1 つの型から別の型に変換できます。キャストなしでポインターの型を変換しようとすると、コンパイラーは警告しますが、void *ポインターは、C 全体で「ジェネリック」ポインターを示すために使用されるため、キャストなしでいつでも変換できます。

*: これが当てはまらないいくつかの奇妙なプラットフォームがあり、実際には異なるタイプのポインターが異なるサイズである場合があります。ただし、それらのいずれかを使用している場合は、それについて知っているでしょう. おそらく、そうではありません。

于 2012-05-03T18:22:44.640 に答える
0
  1. constコードが指しているオブジェクト内の何かを変更しようとすると、コンパイル エラーが発生するために使用されます。これは、プログラマーがオブジェクトを読み取るだけで、変更するつもりがない場合の安全機能です。

  2. **それが関数に渡されたものでなければならないため、使用されます。それをそうではないものとして再宣言することは、重大なプログラミング エラーです。

  3. ポインタは単なるアドレスです。最新のほとんどすべての CPU では、すべてのアドレスが同じサイズ (32 ビットまたは 64 ビット) です。ポインターをある型から別の型に変更しても、実際には値は変更されません。そのアドレスにあるものを別のデータのレイアウトと見なすように言います。

于 2012-05-03T18:19:36.463 に答える