C/C++の値渡し・参照の値渡し、ポインタ

C/C++ 初心者なので、まだよく間違う。
よく間違うこととか、迷い迷い恐る恐る使っていることをメモ。


○ 値渡し・参照の値渡し 問題 (ポインタを使うかどうか。)
C、C++だと、値渡しするか、参照の値渡しするかを
自分で決めれるけど、逆にその分、どうしていいか、迷ってしまう初心者。
効率云々でベストなものを使うというのもいいですが、
Lightweight Language世代の自分としては、Convention over configulationで、"こういう風に書く"と決めてもらった方が有難い。


※あと、C++には本当の、参照渡しがあるようです。 引数に渡した値と、"全く同じもの" が仮引数に渡されるものです。関数宣言で、仮引数の型の最後に、&を使うとよいようです。


C言語だと、
なんとなくですが、いろいろ実際のプログラムとか見ると、
Primitiveなデータ (int や char)以外は、
ポインタを扱って参照渡しにするのが一般的なのかな?という気がしています。


つまり、
構造体やクラスを引数にとる関数は、ポインタ変数を仮引数にしていることが多いように見えます。
(パフォーマンスの問題かもしれませんが。)


一方、クラスのメンバ変数(フィールド)には、ポインタは使わないことが多い。(もちろんケースバイケースですが) ポインタ使ってしまうと、いちいち代入演算子や、コピーコンストラクタを定義しないといけなくなってしまうようです。また、デストラクタをきちんと書かないと、知らない間にデータがdeleteされていることも。 もちろん大きなデータでは、ポインタメンバがいいんでしょうが。


とりあえず、その方針で自分の関数を書こうと思います。


○ 静的・自動的、動的なメモリ確保
上記のように、値渡しか、参照渡し(ポインタ) かを決めて、関数を書くとして、
関数に渡す側のデータ (orオブジェクト) は、どういう風に扱うことができるのでしょうか。


すべて静的・自動変数で書き、&演算子で、アドレスをとってくるという方法もよいと思います。(クラスにもこれが行えて驚きですが)


しかし、C/C++では、後述のような方法 (malloc/new) によって、どんなものでも、
動的に確保して、そのアドレスを得ることができます。


malloc や new の使い方 (動的確保の方法)
malloc は動的に、ヒープ領域に、メモリを確保する方法です。
C言語では、Primitiveな型に対しても、構造体のような型に対しても、malloc() が使えるようです。


配列と似ていますが、一番大きなメリットは、realloc により、より大きな領域を確保できることでしょうか。


また、C++では、malloc の変わりに new が使えます。
C++のnewの方が、文法的にすっきりしています。
C++では当然ながら、クラスに対しても使えるようになっています!


・Cの場合
型名* 変数名 = (型名*)malloc(sizeof(型名)) /* void*型からのキャストで、そのメモリ領域の意味が決まる */
型名* 変数名 = (型名*)malloc(sizeof(型名) * n) /*複数要素分、確保*/


C++の場合
型名* 変数名 = new 型名
型名* 変数名 = new 型名[n] /*複数要素分、確保*/



○ 動的に確保できるもの
Primitiveな型 : 上の一つの場合
配列 : 上の複数要素の場合
構造体型 : 上の型を構造体型に


C++
クラス : 上の型をクラス型に (後述のように、静的にも扱えます。)


となんでも動的に確保できるんですね!!! (さらに、どんなものでも、静的・自動的 にも扱える。)


○ 最後に、C++Java(or Ruby)の違い
C++で、驚いたのが、new を使わなくても、オブジェクト(と同等のもの)が使えるということ。


Personクラスがあった場合に、
Person man1;
とするだけど、man1はPersonオブジェクトのように使えるんですね。
つまり、オブジェクトを、静的・自動変数に扱えるんですね。 (この表現は正しくないと思いますが、感覚的には伝わるかと)
コンストラクタで引数が必要な場合は、Person man2(a, b); とすればよい。(この場合、man2 変数が作られたことになる)


あくまで
Person* man2 = new Person;
とすると、動的に確保できるという違いだけなんですね。 へぇーーー。

#include <iostream>
#include <stdio.h>

class Person{
public:
  std::string name;
  int age;
  int myage(){
    return age; 
  }
  std::string myname(){
    return name;
  }
};

int main(){
  Person a; /* 静的・自動的 に書いて */
  Person *pa;
  pa = &a;
  pa->age = 30;
  pa->name = "Taro";

  Person* b = new Person(); /* 動的に書いて */
  b->age = 28;
  b->name = "Jiro";

  std::cout << "My name is " << pa->myname() << ", age is " << pa->myage() << "\n";
  std::cout << "My name is " << b->myname() << ", age is " << b->myage() << "\n";
}