私はポインターと参照の構文と一般的な意味を理解していますが、APIで参照またはポインターを使用することがどの程度適切であるかをどのように判断すればよいですか。
当然ながら、状況によってはどちらか一方が必要になります(operator++
には参照引数が必要です)が、変数が破壊的に渡されることが構文から明らかであるため、一般的にはポインタ(およびconstポインタ)を使用します。
例えば。次のコードでは:
void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
int a = 0;
add_one(a); // Not clear that a may be modified
add_one(&a); // 'a' is clearly being passed destructively
}
ポインタを使用すると、何が起こっているのかが常に(より)明らかになります。したがって、APIなど、明確さが大きな関心事である場合、ポインタは参照よりも適切ではありません。それは参照が必要なときにだけ使われるべきであることを意味しますか(例えばoperator++
)?どちらか一方にパフォーマンスの問題がありますか?
編集(期限切れ):
NULL値を許可し、生の配列を扱うこと以外に、選択は個人的な好みにかかっているようです。私は、以下の答えを受け入れました。 GoogleのC++スタイルガイド は、「参照は混乱を招く可能性があります。 msgstr "ポインタセマンティクス"#:。
NULLであってはならないポインタ引数をサニタイズするのに必要な追加作業のため(例えばadd_one(0)
はポインタバージョンを呼び出し、実行時に中断します)、オブジェクトが存在しなければならないところで参照を使用することは保守性の観点から意味があります構文上の明快さを失うのは恥です。
できる限り参照を使用し、必要な場所にはポインタを使用してください。
あなたができなくなるまでポインタを避けてください。
その理由は、ポインタによって物事を追いかけたり読んだりすることが難しくなり、安全性が低下し、他のどの構造よりもはるかに危険な操作が行われるためです。
したがって、経験則では、他に選択肢がない場合にのみポインターを使用します。
たとえば、オブジェクトへのポインタを返すことは、関数がnullptrを返すことがある場合に有効なオプションであり、そうであると想定されています。そうは言っても、より良い選択肢はboost::optional
のようなものを使うことでしょう。
別の例は、特定のメモリ操作のために生メモリへのポインタを使用することです。コードベース全体のうち危険な部分を制限するために、コードの非常に狭い部分に隠してローカライズする必要があります。
あなたの例では、引数としてポインタを使う意味はありません。
nullptr
を指定すると、undefined-behavior-landに入ります。関数の振る舞いが与えられたオブジェクトの有無にかかわらず動作しなければならないなら、属性としてポインタを使用することはあなたが引数としてnullptr
を渡すことができることを示唆し、それは関数のために罰金です。これはユーザーと実装の間の一種の契約です。
参照は内部的にポインタとして実装されているため、パフォーマンスはまったく同じです。したがって、あなたはそれについて心配する必要はありません。
参照とポインタをいつ使用するかに関して一般に受け入れられている規則はありません。場合によっては、参照を返すか受け入れる必要があります(たとえば、コピーコンストラクタ)が、それ以外の場合は自由に実行できます。私が遭遇したかなり一般的な慣習は、パラメータが既存のオブジェクトを参照しなければならないときに参照を使用し、NULL値が適切なときにポインタを使用することです。
コーディング規約( Googleの のように)は常にポインタ、またはconst参照を使用するべきであると規定しています。振る舞いと値の構文。
From C++ FAQ Lite -
可能な場合は参照を使用し、必要な場合はポインタを使用してください。
「再着席」を必要としないときはいつでも、参照は通常ポインタよりも優先されます。これは通常、参照がクラスのパブリックインタフェースで最も有用であることを意味します。参照は通常、オブジェクトのスキンに表示され、ポインタは内側に表示されます。
上記の例外は、関数のパラメータまたは戻り値が "センチネル"参照(オブジェクトを参照しない参照)を必要とする場合です。これは通常、ポインタを返すか取ること、そしてNULLポインタにこの特別な意味を与えることによって最もよく行われます(参照は、参照されないNULLポインタではなく、常にオブジェクトをエイリアスにする必要があります)。
注:古い行のCプログラマーは、呼び出し側のコードで明示的ではない参照セマンティクスを提供するため、参照を好まないことがあります。しかし、C++の経験を重ねると、これは情報の隠蔽の一形態であり、負債ではなく資産であることがすぐにわかります。例えば、プログラマーは機械の言語ではなく問題の言語でコードを書くべきです。
私の経験則は、
&
を使わなければなりません)const
にしてください)const T&
)ではない場合は、受信パラメータの参照を使用してください。int ¤t = someArray[i]
)どちらを使用する場合でも、機能が明確でない場合は、機能とそのパラメータの意味を文書化することを忘れないでください。
NULL
/nullptr
である変数が本当に有効な状態でない限り、常に参照を使用してください。
このテーマに関するJohn Carmackの見解は似ています。
NULLポインタは、少なくとも我々のコードでは、C/C++における最大の問題です。単一の値をフラグとアドレスの両方として使用すると、非常に多くの致命的な問題が発生します。可能な限り、C++参照はポインタよりも優先されるべきです。参照は実際には単なるポインタですが、暗黙的にNOT NULLではないという契約があります。ポインタが参照に変換されたときにNULLチェックを実行すると、その後は問題を無視できます。
http://www.altdevblogaday.com/2011/12/24/static-code-analysis/
ユーザー Bret Kuhns は正しくコメントします。
C++ 11標準が完成しました。このスレッドでは、ほとんどのコードで、参照、shared_ptr、およびunique_ptrの組み合わせを使用して完全に問題ないことを説明する時期が来たと思います。
確かに真実ですが、生のポインタをスマートなポインタに置き換えても問題は残っています。
例えば、std::unique_ptr
とstd::shared_ptr
は両方とも、デフォルトのコンストラクターを介して "空の"ポインターとして構成できます。
...検証しないでそれらを使用するとクラッシュする危険性があることを意味します。これはまさにJ. Carmack氏の議論のすべてです。
それでは、「スマートポインタを関数パラメータとして渡すにはどうすればよいでしょうか」という面白い問題があります。
Jon の の質問に対する答えC++ - boost :: shared_ptr への参照の受け渡し、そしてそれでも、コピーや参照によるスマートポインタの受け渡しは、望むほど明確ではないことを示している(Iデフォルトで "by-reference"を優先しますが、私は間違っているかもしれません。
免責事項:参照がNULLでも "リバウンド"でもないという事実を除いて(それらが別名であるオブジェクトを変更することはできません)、それは本当に好みの問題に帰着するので、私は言いません"これの方が良い"。
そうは言っても、私はこの記事の最後の陳述に同意しません。というのも、このコードでは参照が明確になっていないと思われるからです。あなたの例では、
add_one(&a);
より明確かもしれません
add_one(a);
あなたは、おそらくaの値が変わることを知っているからです。一方、関数のシグネチャは
void add_one(int* const n);
どちらかといえば明確ではありません。nは単一の整数または配列のどちらになるのでしょうか。時々、あなたは(文書化されていない)ヘッダと次のような署名にしかアクセスできない。
foo(int* const a, int b);
一目で解釈するのは簡単ではありません。
つまり、参照は、(前に説明した意味での)割り当てや再バインドが不要な場合は、ポインタと同じくらい優れています。さらに、開発者が配列にのみポインタを使用する場合、関数シグネチャは多少あいまいさが少なくなります。演算子の構文が参照によってはるかに読みやすくなるという事実は言うまでもありません。
それは好みの問題ではありません。これが決定的なルールです。
宣言された範囲内で静的に宣言された変数を参照したい場合は、C++参照を使用すれば完全に安全です。静的に宣言されたスマートポインタについても同様です。参照によるパラメータの受け渡しは、この使用例です。
宣言されている範囲よりも広い範囲から何かを参照する場合は、完全に安全であるために参照カウントスマートポインタを使用する必要があります。
構文上の便宜のために、参照を使用してコレクションの要素を参照できますが、安全ではありません。要素はいつでも削除できます。
コレクションの要素への参照を安全に保持するには、参照カウントスマートポインタを使用する必要があります。
パフォーマンスの違いは非常に小さいため、それほど明確ではないアプローチを使用することは正当化されません。
まず、参照が一般的に優れているという言及されていない1つのケースはconst
参照です。単純ではない型では、const reference
を渡しても一時的なものが作成されるのを避け、(値が変更されないので)あなたが心配する混乱を引き起こすことはありません。ここでは、アドレスを取得して関数に渡すのを見れば値が変わったと思うかもしれないので、人にポインタを渡すことを強いることはあなたが心配している非常に混乱を引き起こします。
いずれにせよ、私は基本的にあなたに同意します。これが関数がしていることであることがそれほど明白でない場合、私は関数がそれらの値を変更するために参照を取るのが好きではありません。その場合も私はポインタを使うことを好みます。
あなたが複雑な型で値を返す必要があるとき、私は参照を好む傾向があります。例えば:
bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative
ここでは、関数名によって、情報を配列に戻すことが明確になります。だから混乱はありません。
参照の主な利点は、それらが常に有効な値を含み、ポインタよりもクリーンであり、余分な構文を必要とせずに多態性をサポートすることです。これらの利点がいずれも当てはまらない場合、ポインタよりも参照を優先する理由はありません。
wiki からコピーしました -
その結果、多くの実装では、参照を通じて自動または静的な存続期間を持つ変数を操作することは、構文的には直接アクセスすることと似ていますが、コストのかかる隠された参照解除操作を伴う可能性があります。参照は、識別子の間接参照レベルをあいまいにするので、C++の構文的に物議をかもしている機能です。つまり、ポインタが通常構文的に目立つCコードとは異なり、C++コードの大きなブロックでは、アクセスされているオブジェクトがローカル変数またはグローバル変数として定義されているか、またはそれが参照(暗黙のポインタ)であるかはすぐにはわかりません。特にコードが参照とポインタを混在させる場合は、他の場所が必要です。この側面は、不十分に書かれたC++コードを読み、デバッグすることをより困難にすることができます(エイリアシングを見てください)。
私はこれに100%賛成します、そしてこれが私があなたがそうするための非常に良い理由があるときだけあなたが参照を使うべきであると私が信じる理由です。
留意すべき点:
ポインターはNULL
にすることができ、参照はNULL
にすることはできません。
参照は使いやすく、値を変更したくない場合はconst
を参照に使用でき、関数内で参照が必要になるだけです。
参照が*
と共に使用される間、ポインタは&
と共に使用されます。
ポインタ算術演算が必要な場合はポインタを使用してください。
Void型int a=5; void *p = &a;
へのポインタを持つことはできますが、void型への参照を持つことはできません。
ポインタと参照
void fun(int *a)
{
cout<<a<<'\n'; // address of a = 0x7fff79f83eac
cout<<*a<<'\n'; // value at a = 5
cout<<a+1<<'\n'; // address of a increment by 4 bytes(int) = 0x7fff79f83eb0
cout<<*(a+1)<<'\n'; // value here is by default = 0
}
void fun(int &a)
{
cout<<a<<'\n'; // reference of original a passed a = 5
}
int a=5;
fun(&a);
fun(a);
whatをいつ使用するかの判断
ポインタ:配列、リンクリスト、ツリーの実装、ポインタ演算用。
参照:関数のパラメータと戻り値の型。
"可能な限り参照を使用する"規則に問題があり、今後の使用のために参照を維持したい場合に発生します。これを例で説明するために、次のようなクラスがあるとします。
class SimCard
{
public:
explicit SimCard(int id):
m_id(id)
{
}
int getId() const
{
return m_id;
}
private:
int m_id;
};
class RefPhone
{
public:
explicit RefPhone(const SimCard & card):
m_card(card)
{
}
int getSimId()
{
return m_card.getId();
}
private:
const SimCard & m_card;
};
最初はRefPhone(const SimCard & card)
コンストラクタ内のパラメータを参照によって渡すのが良い考えのように思えるかもしれません、なぜならそれはコンストラクタに間違った/ nullポインタを渡すことを防ぐからです。それはどういうわけかスタック上の変数の割り当てとRAIIからの恩恵を受けることを奨励します。
PtrPhone nullPhone(0); //this will not happen that easily
SimCard * cardPtr = new SimCard(666); //evil pointer
delete cardPtr; //muahaha
PtrPhone uninitPhone(cardPtr); //this will not happen that easily
しかし、それから一時的なものがあなたの幸せな世界を破壊するようになります。
RefPhone tempPhone(SimCard(666)); //evil temporary
//function referring to destroyed object
tempPhone.getSimId(); //this can happen
そのため、盲目的に参照に固執するならば、無効なポインタを渡す可能性と破壊されたオブジェクトへの参照を格納する可能性とのトレードオフの関係になります。これは基本的に同じ効果があります。
edit:「できる限り参照を使用し、必要な場所にポインタを使用する」という規則に従いました。できない限りポインタを使用しないでください。最も支持され、受け入れられている答えから(他の答えもそう示唆しています)。それは明白であるべきですが、例はそれ自体の参照が悪いことを示すことではありません。しかし、それらはポインタのように誤用される可能性があり、コードに脅威をもたらす可能性があります。
ポインタと参照の間には次のような違いがあります。
これらを考慮して私の現在の規則は以下の通りです。
以下はいくつかのガイドラインです。
組み込みデータ型や小さな構造体など、データオブジェクトが小さい場合は、値で渡します。
データオブジェクトが配列の場合は、ポインタを使用するのが唯一の選択肢です。ポインタをconstへのポインタにします。
データオブジェクトが適切なサイズの構造体である場合は、プログラムの効率を向上させるためにconstポインタまたはconst参照を使用してください。構造体またはクラスデザインをコピーするのに必要な時間とスペースを節約できます。ポインタまたは参照をconstにします。
データオブジェクトがクラスオブジェクトの場合は、const参照を使用します。クラス設計のセマンティクスでは参照を使用する必要があることが多く、これがC++がこの機能を追加した主な理由です。
1.データオブジェクトが組み込みデータ型の場合は、ポインタを使用します。 xがintであるfixit(&x)のようなコードを見つけた場合、この関数がxを変更しようとしていることは明らかです。
2.データオブジェクトが配列の場合は、唯一の選択項目としてポインタを使用します。
3.データオブジェクトが構造体の場合は、参照またはポインタを使用します。
4.データオブジェクトがクラスオブジェクトの場合は、参照を使用します。
もちろん、これらは単なるガイドラインであり、異なる選択をする理由があるかもしれません。たとえば、cinは基本型の参照を使用するので、cin >>&nの代わりにcin >> nを使用できます。
ポインタと参照は同等の速度を持ち、ポインタは参照ができること以上のことをすべて実行できます。どちらを使用するかは、個人的な意見に大きく基づいています。ポインタの機能が本当に必要でない限り、参照を使うのが一般的です。
ちょうど私のダイムを入れる。私はただテストを実行した。卑劣なことだ。参照を使用するのと比較して、ポインタを使用してg ++に同じミニプログラムのアセンブリファイルを作成させるだけです。出力を見ると、それらはまったく同じです。シンボルの命名以外だから(単純な例で)パフォーマンスを見ても問題はありません。
今ポインタ対参照のトピックについて。私見明快さは何よりも重要だと思います。私が暗黙の行動を読むとすぐに私のつま先は丸まり始めます。参照をNULLにできないのは、暗黙のうちに暗黙の動作であることに同意します。
NULLポインタを間接参照しても問題ありません。それはあなたのアプリケーションをクラッシュさせ、そしてデバッグするのを簡単にするでしょう。もっと大きな問題は、無効な値を含む初期化されていないポインタです。これにより、メモリが破損し、Originが明確にならずに未定義の動作が引き起こされる可能性があります。
これが参照がポインタよりはるかに安全だと私が思うところです。そして私は以前の声明に同意します。インターフェース(これは明確に文書化されるべきです。契約による設計Bertrand Meyerを参照してください)が関数に対するパラメータの結果を定義するということです。今これらすべてを考慮に入れて、私の好みは可能な限り/可能な限りいつでも参照を使用することに行きます。
参照はよりクリーンで使いやすく、そして情報を隠すというより良い仕事をします。ただし、参照を再割り当てすることはできません。最初にあるオブジェクトを指してから別のオブジェクトを指す必要がある場合は、ポインタを使用する必要があります。参照をnullにすることはできません。そのため、問題のオブジェクトがnullになる可能性がある場合は、参照を使用しないでください。あなたはポインタを使わなければなりません。あなたが自分自身でオブジェクト操作を扱いたいならば、すなわちあなたがスタックよりむしろヒープ上のオブジェクトのためにメモリ空間を割り当てたいならば、あなたはポインタを使わなければなりません
int *pInt = new int; // allocates *pInt on the Heap
一般的にメンバ変数は、決して意味がないので、決して参照にすべきではありません。代入演算子を指定しないと、クラスは割り当て不可能になります。また、いったんあるオブジェクトを参照するようにメンバー参照を設定すると、別のオブジェクトを参照するためにそのメンバーを変更することはできません。参照の最も適切な使用法は、参照渡しを可能にする関数パラメータとして使用することです。