web-dev-qa-db-ja.com

typedefポインターを使用するのは良い考えですか?

私はいくつかのコードに目を通し、慣例は次のようなポインタ型を変えることであることに気づきました

SomeStruct* 

typedef SomeStruct* pSomeStruct;

これにメリットはありますか?

68
Unknown

これは、ポインタ自体を「ブラックボックス」、つまり内部表現がコードとは無関係なデータの一部と見なすことができる場合に適切です。

基本的に、コードがneverポインターの逆参照を行い、API関数を参照する(参照する)場合、typedefはコード内の*sの数を減らすだけでなく、 、しかし、ポインタを実際に干渉するべきではないことをプログラマに示唆しています。

これにより、将来必要に応じてAPIを簡単に変更できます。たとえば、ポインターではなくIDを使用するように変更した場合(またはその逆)、最初にポインターが逆参照されることはなかったため、既存のコードは破損しません。

99
Artelius

私の経験ではありません。 '* 'はコードを読みにくくします。

69
sigjuice

Typedef内でポインターを使用するのは、関数へのポインターを処理するときだけです。

_typedef void (*SigCatcher(int, void (*)(int)))(int);
_
_typedef void (*SigCatcher)(int);

SigCatcher old = signal(SIGINT, SIG_IGN);
_

さもなければ、私はそれらが役に立つよりも混乱していると思います。


取り消し線の宣言は、シグナルキャッチャーではなく、signal()関数へのポインターの正しい型です。 (上記の修正されたSigCatcherタイプを使用して)以下のように記述することで、より明確にすることができます。

_ typedef SigCatcher (*SignalFunction)(int, SigCatcher);
_

または、signal()関数を宣言するには:

_ extern SigCatcher signal(int, SigCatcher);
_

つまり、SignalFunctionは、2つの引数(intSigCatcher)を取り、SigCatcherを返す関数へのポインタです。そして、signal()自体は、2つの引数(intSigCatcher)を取り、SigCatcherを返す関数です。

28

これは、いくつかのエラーを回避するのに役立ちます。たとえば、次のコードでは:

int* pointer1, pointer2;

pointer2はint *ではなく、単純なintです。しかし、typedefではこれは起こりません。

typedef int* pInt;
pInt pointer1, pointer2;

両方ともint *になりました。

14
Sad Developer

私の答えは明確な「いいえ」です。

どうして?

まず、最初に、単一の文字_*_を別の単一の文字pと交換するだけです。それはzeroゲインです。これだけでは、意味のない余分なことを行うのは常に悪いため、これを行うことはできません。

第二に、それが重要な理由です。_*_には、隠すのが良くない意味があります。このような関数に何かを渡すと

_void foo(SomeType bar);

void baz() {
    SomeType myBar = getSomeType();
    foo(myBar);
}
_

myBarの意味は、foo()に渡すことで変更されるとは思わない。結局のところ、私は値で渡しているので、foo()myBarのコピーしか見ることができません。 SomeTypeが何らかのポインターを意味するようにエイリアスされているときは違います!

これは、CポインターとC++スマートポインターの両方に適用されます。ユーザーへのポインターであるという事実を隠すと、まったく不要な混乱が生じます。そのため、ポインタのエイリアスを作成しないでください。

(ポインタ型を型定義する習慣は、プログラマとして持っている星の数を隠そうとする見当違いの試みだと思います http://wiki.c2.com/?ThreeStarProgrammer 。)

12
cmaster

それは(非常に多くの答えのように)依存します。

Cでは、オブジェクトがポインターであることを偽装しようとしているため、これは非常に一般的です。これは、すべての関数が操作するオブジェクトであることを暗示しようとしています(下にあるのはポインターですが、操作しているオブジェクトを表します)。

MYDB   db = MYDBcreateDB("Plop://djdjdjjdjd");

MYDBDoSomthingWithDB(db,5,6,7);
CallLocalFuc(db); // if db is not a pointer things could be complicated.
MYDBdestroyDB(db);

MYDBの下には、おそらく何らかのオブジェクトへのポインターがあります。

C++では、これはもう必要ありません。
主に、参照によって物を渡すことができ、メソッドがクラス宣言に組み込まれているためです。

MyDB   db("Plop://djdjdjjdjd");

db.DoSomthingWithDB(5,6,7);
CallLocalFuc(db);   // This time we can call be reference.
db.destroyDB();     // Or let the destructor handle it.
5
Martin York

これはスタイルの問題です。この種のコードは、Windowsヘッダーファイルに頻繁に表示されます。小文字のpを前に付けるよりも、すべて大文字のバージョンを好む傾向がありますが。

個人的には、このtypedefの使用を避けています。ユーザーにPFooよりもFoo *が必要であることを明示的に伝える方がはるかに明確です。 TypedefはSTLを読みやすくするために最近最適です:)

typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;
5
JaredPar

関心のある言語がCであると仮定した議論。C++の分岐は考慮されていません。

タグなし構造にポインターtypedefを使用する

質問 ポインターとして定義されている構造体のサイズ は、(構造)ポインターにtypedefを使用する際の興味深い側面を浮き彫りにします。

タグレスコンクリート(不透明ではない)構造型定義を検討します。

_typedef struct { int field1; double field2; } *Information;
_

メンバーの詳細は、この議論に完全に接しています。重要なのは、これが_typedef struct tag *tag;_のような不透明型ではないことです(タグなしでtypedefを介してこのような不透明型を定義することはできません)。

提起された質問は、「その構造のサイズをどのようにして見つけることができますか?」です。

短い答えは「型の変数を介してのみ」です。 sizeof(struct tag)で使用するタグはありません。うまく書けない sizeof(*Information)、たとえば、sizeof(Information *)は、構造体型のサイズではなく、ポインター型へのポインターのサイズです。

実際、このような構造を割り当てたい場合は、動的割り当て(または動的割り当てを模倣する代理技術)を除いて、構造を作成することはできません。ポインターがInformationと呼ばれる構造型のローカル変数を作成する方法も、構造型のファイルスコープ(グローバルまたはstatic)変数を作成する方法もありません。また、(そのような構造へのポインタとは対照的に)そのような構造を別の構造またはユニオン型に埋め込む方法もありません。

あなたはできる-しなければならない-書く:

_Information info = malloc(sizeof(*info));
_

ポインタがtypedefに隠されているという事実は別として、これは良い習慣です。_infoのタイプが変わっても、サイズの割り当ては正確なままです。ただし、この場合、構造のサイズを取得して構造を割り当てる唯一の方法でもあります。そして、構造のインスタンスを作成する他の方法はありません。

これは有害ですか?

それはあなたの目標次第です。

これは不透明型ではありません。ポインタ型がtypedef 'dの場合、構造の詳細を定義する必要があります。

これは、動的メモリ割り当てでのみ使用できるタイプです。

名前のないタイプです。構造タイプへのポインターには名前がありますが、構造タイプ自体にはありません。

動的な割り当てを強制する場合、これがその方法のようです。

しかし、全体としては、啓蒙よりも混乱と不安を引き起こす可能性が高くなります。

概要

一般に、typedefを使用してタグレス構造タイプへのポインタを定義することはお勧めできません。

4

Typedefはコードを読みやすくするために使用されますが、typedefとしてポインターを作成すると混乱が増えます。 typedefポインターを避ける方が良い。

4
Chand

これを行うと、コンパイラーは以下を読み取るため、const pSomeStructのSTLコンテナーを作成できません。

list<const pSomeStruct> structs;

なので

list<SomeStruct * const> structs;

要素は割り当て可能でないため、これは正当なSTLコンテナではありません。

こちらをご覧ください question

3
Dan Hook

番号。

constと混ぜた瞬間にあなたの人生が悲惨になります

typedef foo *fooptr;
const fooptr bar1;
const foo *bar2

bar1およびbar2同じタイプ?

そして、ええ、私はちょうどハーブサッターの達人を引用しています。彼女は多くの真実を話しました。 ;)

-編集-

引用記事へのリンクを追加します。

http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835

2
dgnuff

Typedefの目的は実装の詳細を非表示にすることですが、ポインタープロパティをtypedefすることはあまりにも多くを非表示にし、コードの読み取り/理解を困難にします。だからそれをしないでください。


実装の詳細を隠したい場合(これは良いことです)、ポインター部分を隠さないでください。標準のFILEインターフェイスのプロトタイプを例に取ります:

FILE *fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, FILE *stream);

ここで、fopenはpointerをいくつかの構造体FILE(実装の詳細がわからない)に返します。たぶんFILEはそれほど良い例ではありません。なぜなら、この場合、それがポインターであるという事実を隠したpFILEタイプで動作する可能性があるからです。

pFILE fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, pFILE stream);

ただし、直接指定されたコンテンツを操作することはないため、これは機能します。あなたがいくつかの場所でコードを変更するポインターをtypedefする瞬間は、私の経験では読むのが非常に難しくなります。

1
hlovdal

Win32 APIは、ほぼすべての構造(すべてではない場合)でこれを行います

POINT => *LPPOINT
WNDCLASSEX => *LPWNDCLASSEX
RECT => *LPRECT
PRINT_INFO_2 => *LPPRINT_INFO_2

一貫性があるのは素晴らしいことですが、私の意見では、優雅さを追加するものではありません。

1
dreamlax

しばらく前、私はこの質問に「いいえ」と答えていました。現在、スマートポインターの出現により、ポインターは常にスター「*」で定義されているとは限りません。したがって、型がポインターであるかどうかは明らかではありません。

だから今私は言うだろう:それが「ポインタ型」であることを非常に明確にされている限り、typedefポインタは問題ありません。つまり、そのために特別にプレフィックス/サフィックスを使用する必要があります。いいえ、たとえば、「p」は十分なプレフィックスではありません。おそらく「ptr」で行くと思います。

0
Benoît