C ++プログラミングは初めてですが、Javaの経験があります。C ++で関数にオブジェクトを渡す方法に関するガイダンスが必要です。
ポインター、参照、または非ポインターと非参照の値を渡す必要がありますか?オブジェクトへの参照を保持する変数のみを渡すため、Javaではそのような問題はありません。
これらの各オプションをどこで使用するかについても説明していただければ幸いです。
C ++プログラミングは初めてですが、Javaの経験があります。C ++で関数にオブジェクトを渡す方法に関するガイダンスが必要です。
ポインター、参照、または非ポインターと非参照の値を渡す必要がありますか?オブジェクトへの参照を保持する変数のみを渡すため、Javaではそのような問題はありません。
これらの各オプションをどこで使用するかについても説明していただければ幸いです。
回答:
値渡し、ただし次の場合を除く
const
参照渡しします。const
左辺値以外の参照によるパスを使用します。const
参照渡しするかどうかを決定します。)ポインタを渡すことは実質的に推奨されません。オプションのパラメーターはstd::optional
(boost::optional
古いstdライブラリの場合)として最もよく表現され、エイリアスは参照により正常に行われます。
C ++ 11の移動のセマンティクスにより、複雑なオブジェクトの場合でも、値による受け渡しと戻りがさらに魅力的になります。
引数をconst
参照渡しする場合を除き、
const
参照で渡しますNULL
/ 0
/ nullptr
代わりに、前のルールを適用して、引数へのポインターを渡すかどうかを決定しconst
ます(ここでは、値渡しは常にC ++ 03でコピーを作成するため、「値渡し」は「コピー渡し」と呼ばれます)
これには他にもありますが、これらのいくつかの初心者のルールはかなり遠くまで届きます。
C ++とJavaの呼び出し規約にはいくつかの違いがあります。C ++では、技術的に言えば2つの規則しかありません。値渡しと参照渡しで、3番目のポインター渡し規則(実際にはポインター型の値渡し)を含むいくつかの文献があります。その上で、引数の型に一定性を追加して、セマンティクスを強化できます。
参照渡し
参照渡しとは、関数が概念的にはオブジェクトインスタンスを受け取り、そのコピーではないことを意味します。参照は、概念的には、呼び出しコンテキストで使用されたオブジェクトのエイリアスであり、nullにすることはできません。関数内で実行されるすべての操作は、関数外のオブジェクトに適用されます。この規則は、JavaまたはCでは使用できません。
値渡し(およびポインター渡し)
コンパイラーは、呼び出しコンテキストでオブジェクトのコピーを生成し、関数内でそのコピーを使用します。関数内で実行されるすべての操作は、外部要素ではなくコピーに対して行われます。これは、Javaのプリミティブ型の規則です。
その特別なバージョンは、ポインタ(オブジェクトのアドレス)を関数に渡します。関数はポインターを受け取り、ポインター自体に適用されたすべての操作はコピー(ポインター)に適用されます。一方、逆参照されたポインターに適用された操作は、そのメモリ位置のオブジェクトインスタンスに適用されるため、関数副作用がある可能性があります。オブジェクトへのポインターの値渡しを使用する効果により、参照渡しの場合と同様に、内部関数が外部値を変更でき、オプションの値も許可されます(ヌルポインターを渡す)。
これは、関数が外部変数を変更する必要がある場合にCで使用される規則であり、参照タイプでJavaで使用される規則です。参照はコピーされますが、参照されるオブジェクトは同じです:参照/ポインターへの変更は外部からは見えません関数ですが、ポイントされたメモリへの変更があります。
方程式にconstを追加する
C ++では、さまざまなレベルで変数、ポインター、参照を定義するときに、オブジェクトに定数を割り当てることができます。変数を定数として宣言し、定数インスタンスへの参照を宣言して、定数オブジェクトへのすべてのポインター、可変オブジェクトへの定数ポインター、および定数要素への定数ポインターを定義できます。逆に、Javaでは1つのレベルの定数(最終キーワード)しか定義できません:変数(プリミティブ型のインスタンス、参照型の参照)のレベルですが、不変要素への参照は定義できません(クラス自体が不変)。
これは、C ++呼び出し規約で広く使用されています。オブジェクトが小さい場合、値でオブジェクトを渡すことができます。コンパイラーはコピーを生成しますが、そのコピーは高価な操作ではありません。その他のタイプの場合、関数がオブジェクトを変更しない場合は、タイプの定数インスタンス(通常は定数参照と呼ばれます)への参照を渡すことができます。これはオブジェクトをコピーしませんが、関数に渡します。しかし同時に、コンパイラーはオブジェクトが関数内で変更されないことを保証します。
経験則
これは従うべきいくつかの基本的なルールです:
これらのルールには他にも小さな逸脱がありますが、最初のルールはオブジェクトの所有権の処理です。オブジェクトがnewで動的に割り当てられる場合、削除(またはその[]バージョン)で割り当てを解除する必要があります。オブジェクトの破棄を担当するオブジェクトまたは関数は、リソースの所有者と見なされます。動的に割り当てられたオブジェクトがコードの一部で作成されたが、所有権が別の要素に転送された場合、通常はポインター渡しのセマンティクスで、または可能であればスマートポインターで行われます。
サイドノート
C ++とJavaの参照の違いの重要性を主張することが重要です。C ++では、参照は概念的にはオブジェクトのインスタンスであり、オブジェクトへのアクセサーではありません。最も単純な例は、スワップ関数の実装です。
// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
Type tmp = a;
a = b;
b = tmp;
}
int main() {
Type a, b;
Type old_a = a, old_b = b;
swap( a, b );
assert( a == old_b );
assert( b == old_a );
}
上記のswap関数は、参照を使用して両方の引数を変更します。Javaの最も近いコード:
public class C {
// ...
public static void swap( C a, C b ) {
C tmp = a;
a = b;
b = tmp;
}
public static void main( String args[] ) {
C a = new C();
C b = new C();
C old_a = a;
C old_b = b;
swap( a, b );
// a and b remain unchanged a==old_a, and b==old_b
}
}
コードのJavaバージョンは、参照のコピーを内部で変更しますが、実際のオブジェクトを外部で変更しません。Java参照は、値によって関数に渡されるポインター演算のないCポインターです。
考慮すべきいくつかのケースがあります。
void modifies(T ¶m);
// vs
void modifies(T *param);
このケースは主にスタイルに関するものです。コードをcall(obj)またはcall(&obj)のようにしたいですか?ただし、違いが問題になる2つのポイントがあります。以下のオプションのケースと、演算子をオーバーロードするときに参照を使用する場合です。
void modifies(T *param=0); // default value optional, too
// vs
void modifies();
void modifies(T ¶m);
void uses(T const ¶m);
// vs
void uses(T param);
これは興味深いケースです。経験則では、「安価なコピー」タイプは値で渡されます。これらは一般に小さいタイプですが(常にではない)、他のタイプはconst refで渡されます。ただし、関数内でコピーを作成する必要がある場合は、値で渡す必要があります。(はい、これは実装の詳細を少し公開します。C'estle C ++。)
void uses(T const *param=0); // default value optional, too
// vs
void uses();
void uses(T const ¶m); // or optional(T param)
ここですべての状況の違いが最も少ないので、あなたの人生を最も簡単にするものを選択してください。
void f(T);
void f(T const);
これらの宣言は実際にはまったく同じ関数です! 値で渡す場合、constは純粋に実装の詳細です。 やってみよう:
void f(int);
void f(int const) { /* implements above function, not an overload */ }
typedef void NC(int); // typedefing function types
typedef void C(int const);
NC *nc = &f; // nc is a function pointer
C *c = nc; // C and NC are identical types
const
値渡しの場合、実装であることを知りませんでした。
void func (vector v)
関数が環境から完全に分離する必要がある場合は、変数を値で渡します。つまり、関数が元の変数を変更するのを防ぎ、関数の実行中に他のスレッドがその値を変更するのを防ぎます。
欠点は、オブジェクトのコピーに費やされるCPUサイクルと追加のメモリです。
void func (const vector& v);
このフォームは、コピーのオーバーヘッドを削除しながら、値渡しの動作をエミュレートします。関数は元のオブジェクトへの読み取りアクセス権を取得しますが、その値を変更することはできません。
欠点はスレッドセーフティです。別のスレッドによって元のオブジェクトに加えられた変更は、関数の実行中に関数内に表示されます。
void func (vector& v)
これは、関数が変数に何らかの値を書き戻す必要がある場合に使用します。変数は最終的に呼び出し元によって使用されます。
const参照の場合と同様に、これはスレッドセーフではありません。
void func (const vector* vp);
構文が異なることを除いて、機能的にconst-reference by passと同じです。さらに、呼び出し側の関数がNULLポインターを渡して、渡す有効なデータがないことを示すことができます。
スレッドセーフではありません。
void func (vector* vp);
非const参照に似ています。関数が値を書き戻すことを想定していない場合、呼び出し元は通常、変数をNULLに設定します。この規則は、多くのglibc APIで見られます。例:
void func (string* str, /* ... */) {
if (str != NULL) {
*str = some_value; // assign to *str only if it's non-null
}
}
すべてがスレッドセーフではなく、参照/ポインタで渡されるように。
誰も私がそれに追加していることを述べていないので、オブジェクトをc ++の関数に渡すときに、オブジェクトのクローンを作成してメソッドに渡すものがない場合は、オブジェクトのデフォルトのコピーコンストラクターが呼び出されます。元のオブジェクトの代わりにオブジェクトのコピーに反映されるオブジェクト値を変更すると、それはc ++の問題です。したがって、すべてのクラス属性をポインターにすると、コピーコンストラクターはアドレスをコピーします。ポインター属性。そのため、ポインター属性アドレスに格納された値を操作するオブジェクトでメソッドを呼び出すと、変更はパラメーターとして渡された元のオブジェクトにも反映されるため、Javaと同じように動作できますが、すべてのクラスを忘れないでください。属性はポインタでなければなりません。また、ポインタの値を変更する必要があります。コードの説明で非常に明確になります。
Class CPlusPlusJavaFunctionality {
public:
CPlusPlusJavaFunctionality(){
attribute = new int;
*attribute = value;
}
void setValue(int value){
*attribute = value;
}
void getValue(){
return *attribute;
}
~ CPlusPlusJavaFuncitonality(){
delete(attribute);
}
private:
int *attribute;
}
void changeObjectAttribute(CPlusPlusJavaFunctionality obj, int value){
int* prt = obj.attribute;
*ptr = value;
}
int main(){
CPlusPlusJavaFunctionality obj;
obj.setValue(10);
cout<< obj.getValue(); //output: 10
changeObjectAttribute(obj, 15);
cout<< obj.getValue(); //output: 15
}
しかし、これは、メモリリークが発生しやすく、デストラクタを呼び出すことを忘れないようにするポインタに関連する多くのコードを作成することになるため、良い考えではありません。そして、このc ++を回避するために、ポインターを含むオブジェクトが関数の引数に渡されて他のオブジェクトデータの操作が停止したときに新しいメモリを作成するコピーコンストラクターがあります。Javaは値渡しを行い、値は参照であるため、コピーコンストラクターは必要ありません。
オブジェクトをパラメーターとして関数に渡す方法は3つあります。
次の例を見てください。
class Sample
{
public:
int *ptr;
int mVar;
Sample(int i)
{
mVar = 4;
ptr = new int(i);
}
~Sample()
{
delete ptr;
}
void PrintVal()
{
cout << "The value of the pointer is " << *ptr << endl
<< "The value of the variable is " << mVar;
}
};
void SomeFunc(Sample x)
{
cout << "Say i am in someFunc " << endl;
}
int main()
{
Sample s1= 10;
SomeFunc(s1);
s1.PrintVal();
char ch;
cin >> ch;
}
出力:
私はsomeFuncにいるとしましょう
ポインター
の値は-17891602 です変数の値は4です
以下は、C ++で関数に引数/パラメーターを渡す方法です。
1.値による。
// passing parameters by value . . .
void foo(int x)
{
x = 6;
}
2.参照により。
// passing parameters by reference . . .
void foo(const int &x) // x is a const reference
{
x = 6;
}
// passing parameters by const reference . . .
void foo(const int &x) // x is a const reference
{
x = 6; // compile error: a const reference cannot have its value changed!
}
3.オブジェクトごと。
class abc
{
display()
{
cout<<"Class abc";
}
}
// pass object by value
void show(abc S)
{
cout<<S.display();
}
// pass object by reference
void show(abc& S)
{
cout<<S.display();
}