C ++でオブジェクトを関数に渡す方法は?


249

C ++プログラミングは初めてですが、Javaの経験があります。C ++で関数にオブジェクトを渡す方法に関するガイダンスが必要です。

ポインター、参照、または非ポインターと非参照の値を渡す必要がありますか?オブジェクトへの参照を保持する変数のみを渡すため、Javaではそのような問題はありません。

これらの各オプションをどこで使用するかについても説明していただければ幸いです。


6
どの本からC ++を学びますか?

17
その本は強くお勧めしません。Stan LippmanによるC ++ Primerを選びます。
Prasoon Saurav、2010年

23
さて、あなたの問題があります。Schildtは基本的にcr * pです-Koenig&MooによるAccelerated C ++を入手してください。

9
Bjarne StroustrupによるC ++プログラミング言語について誰も触れなかったのではないでしょうか。Bjarne StroustrupはC ++の作成者です。C ++を学ぶのにとても良い本です。
George

15
@George:TC ++ PLは初心者向けではありませんが、C ++。xDの聖書と見なされています
Prasoon Saurav

回答:


277

C ++ 11の経験則

渡し、ただし次の場合を除く

  1. オブジェクトの所有権は必要ありません。単純なエイリアスが必要です。その場合const参照渡しします
  2. オブジェクトを変更する必要があります。その場合は、const左辺値以外の参照によるパスを使用します
  3. 派生クラスのオブジェクトを基本クラスとして渡す場合、その場合は参照渡しする必要があります。(前のルールを使用して、const参照渡しするかどうかを決定します。)

ポインタを渡すことは実質的に推奨されません。オプションのパラメーターはstd::optionalboost::optional古いstdライブラリの場合)として最もよく表現され、エイリアスは参照により正常に行われます。

C ++ 11の移動のセマンティクスにより、複雑なオブジェクトの場合でも、値による受け渡しと戻りがさらに魅力的になります。


C ++ 03の経験則

引数const参照渡しする場合を除き、

  1. それらは関数内で変更され、そのような変更は外部に反映される必要があります。その場合、非const参照で渡します
  2. 機能は、ユーザーが渡すことができるように、あなたはポインタで通過する場合には任意の引数なしで呼び出し可能でなければなりませんNULL/ 0/ nullptr代わりに、前のルールを適用して、引数へのポインターを渡すかどうかを決定constます
  3. それらは組み込み型であり、コピー渡すことができます
  4. それらは関数内で変更され、そのような変更は外部に反映されるべきでありません。その場合、コピーで渡すことができます(前のルールに従って渡し、関数内でコピーを作成することもできます)

(ここでは、値渡しは常にC ++ 03でコピーを作成するため、「値渡し」は「コピー渡し」と呼ばれます)


これには他にもありますが、これらのいくつかの初心者のルールはかなり遠くまで届きます。


17
+1-また、関数内で変更されるオブジェクトは非const参照ではなくポインタを介して渡す必要があると感じている人もいます(Googleなど)。その理由は、オブジェクトのアドレスが関数に渡されたときに、その関数がそれを変更する可能性があることはより明白であるということです。例:参照の場合、呼び出しはfoo(bar);です。参照がconstであるかどうかに関係なく、ポインタではfoo(&bar)です。そして、fooが可変オブジェクトに渡されていることはより明白です。
RC。

19
@RCは、ポインタがconstかどうかをまだ通知しません。Googleのガイドラインは、さまざまなC ++オンラインコミュニティの多くの問題に対応しています。

14
他のコンテキストではgoogleが先導しているかもしれませんが、C ++ではスタイルガイドはそれほど良くありません。
DavidRodríguez-

4
@ArunSaha:純粋なスタイルガイドとして、Stroustrupには航空宇宙会社向けに開発されたガイドがあります。Googleガイドを閲覧したところ、いくつかの理由で気に入らなかった。Sutter&Alexandrescu C ++ Coding Standardsは読むのに最適な本であり、多くの良いアドバイスを得ることができますが、実際にはスタイルガイドではありません。人間と常識を除いて、styleの自動チェッカーは知りません。
デビッドロドリゲス-

3
@anonただし、引数がポインターを介して渡されない場合は、変更されないことが保証されます。これは非常に貴重なIMHOです。それ以外の場合は、関数内の変数に何が起こるかを追跡するときに、渡されたすべての関数のヘッダーファイルを調べて、変更されているかどうかを判断する必要があります。このようにして、ポインタを介して渡されたものだけを見る必要があります。
smehmood 2011年

107

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ポインターです。


4
@ david-rodriguez-dribeas私は経験則のセクション、特に「プリミティブ型には値による
受け渡しを優先する

私によると、これは質問に対するはるかに良い答えです。
unrealsoul007 2018

22

考慮すべきいくつかのケースがあります。

パラメータが変更されました( "out"および "in / out"パラメータ)

void modifies(T &param);
// 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 &param);

パラメータは変更されていません

void uses(T const &param);
// 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 &param);  // 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

3
+1 const値渡しの場合、実装であることを知りませんでした。
balki 2012

20

値渡し:

void func (vector v)

関数が環境から完全に分離する必要がある場合は、変数を値で渡します。つまり、関数が元の変数を変更するのを防ぎ、関数の実行中に他のスレッドがその値を変更するのを防ぎます。

欠点は、オブジェクトのコピーに費やされるCPUサイクルと追加のメモリです。

const参照で渡す:

void func (const vector& v);

このフォームは、コピーのオーバーヘッドを削除しながら、値渡しの動作をエミュレートします。関数は元のオブジェクトへの読み取りアクセス権を取得しますが、その値を変更することはできません。

欠点はスレッドセーフティです。別のスレッドによって元のオブジェクトに加えられた変更は、関数の実行中に関数内に表示されます。

非const参照で渡す:

void func (vector& v)

これは、関数が変数に何らかの値を書き戻す必要がある場合に使用します。変数は最終的に呼び出し元によって使用されます。

const参照の場合と同様に、これはスレッドセーフではありません。

constポインタを渡します。

void func (const vector* vp);

構文が異なることを除いて、機能的にconst-reference by passと同じです。さらに、呼び出し側の関数がNULLポインターを渡して、渡す有効なデータがないことを示すことができます。

スレッドセーフではありません。

非constポインターを渡す:

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
    }
}

すべてがスレッドセーフではなく、参照/ポインタで渡されるように。


0

誰も私がそれに追加していることを述べていないので、オブジェクトを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は値渡しを行い、値は参照であるため、コピーコンストラクターは必要ありません。


-1

オブジェクトをパラメーターとして関数に渡す方法は3つあります。

  1. 参照渡し
  2. 値渡し
  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です


メソッドは2つしかありません(最初の2つはあなたが言及したものです)。「パラメータに定数を渡す」とはどういう意味かわかりません
MM

-1

以下は、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();
}

1
「オブジェクトによる受け渡し」は重要ではありません。値渡しと参照渡しのみがあります。「ケース3」は、実際には値渡しの1つのケースと参照渡しの1つのケースを示しています。
2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.