質問自体には大きな誤解があると思います。
一方ではスタックまたはヒープに割り当てられたオブジェクトと、他方では値または参照またはポインターを渡すことの間に関係はありません。
スタックとヒープの割り当て
可能な場合は常にスタックを優先します。そうすれば、オブジェクトの存続期間が管理され、処理がはるかに簡単になります。
ただし、いくつかの状況では不可能な場合があります。
- 仮想建設(工場を考えてください)
- 共有所有権(ただし、常に回避するようにしてください)
そして、私はいくつかを見逃すかもしれませんが、この場合、例えばスマートポインターを使用することによって、スタックの存続期間管理機能を活用するためにSBRM(スコープバウンドリソース管理)を使用する必要があります。
通過:値、参照、ポインター
まず第一に、セマンティクスの違いがあります:
- value、const reference:渡されたオブジェクトはメソッドによって変更されません
- 参照:渡されたオブジェクトはメソッドによって変更される可能性があります
- ポインター/定数ポインター:(動作の)参照と同じですが、nullの可能性があります
一部の言語(Haskellのような機能的な種類)は、デフォルトで参照/ポインターを提供しないことに注意してください。作成された値は不変です。外部環境に対処するためのいくつかの回避策は別として、それらはこの使用によってそれほど制限されておらず、それはどういうわけかデバッグを容易にします。
友達は、参照渡しやポインタ渡しにはまったく問題がないことを知っておく必要があります。たとえばswap
、値渡しでは実装できません。
最後に、ポリモーフィズムでは値渡しのセマンティクスは許可されていません。
それでは、パフォーマンスについてお話しましょう。
ビルトインは(間接参照を避けるために)値で渡され、ユーザー定義の大きなクラスは(コピーを避けるために)参照/ポインターで渡される必要があることは通常よく受け入れられています。実際、大きいということは、一般的に、コピーコンストラクターが簡単ではないことを意味します。
ただし、小さなユーザー定義クラスに関しては未解決の質問があります。最近公開されたいくつかの記事は、場合によっては、値渡しによってコンパイラからの最適化が改善される可能性があることを示唆しています。たとえば、この場合は次のようになります。
Object foo(Object d) { d.bar(); return d; }
int main(int argc, char* argv[])
{
Object o;
o = foo(o);
return 0;
}
o
ここで、スマートコンパイラは、コピーせずにその場で変更できることを判断できます。(関数定義が表示されている必要があると思いますが、リンク時最適化がそれを理解するかどうかはわかりません)
したがって、パフォーマンスの問題には、いつものように1つの可能性しかありません。