##仮引数(parameter)の3つのパターン
C++のコードを眺めていると以下のような3つのパターンの構造体、クラスの仮引数に出くわします。
//①
void hoge( TEST test)
{
test.a++;
}
//②
void hoge( TEST *test)
{
test->a++;
}
//③
void hoge( TEST &test)
{
test.a++;
}
Cでは①②のパターン、
C#では①のパターンのみ出くわします。
上記3つのパターンの仮引数は各々の言語で以下のように分類されます。
C | C++ | C# | |
---|---|---|---|
値 | ①(※) | ①(※) | ①(TESTが構造体の場合) |
参照 | ② | ②③ | ①(TESTがクラスの場合) |
特に、①はC#は一見、値のように見えて(TESTがクラスの場合)参照されているということに注意してください。
実は①はC/C++の場合も値ではないかもしれません。
##※構造体,クラスは基本的に値渡しされない
基本的にサイズが大きい構造体,クラスは値型として引数がコピーされるのではなく、
ポインタ渡し、または参照渡しをして使用されることが多いです。
よって、C/C++で①が使われることはほぼありません。
それにもかかわらず、①が使われる場面がCで割と出てきます。
そういうときは、
Cの場合、
typedef void* TEST;
voidポインタがTESTとして新しい名前に定義されている。
または
typedef struct {
int a;
} *TEST;
structのポインタがTESTとして新しい名前に定義されている。
C++の場合、
typedef shared_ptr<foo> TEST;
fooのポインタがTESTとして新しい名前に定義されている。
というように一見値型のように見えてポインタ型として、どこかで再定義されています。
(区別できるように、Cの場合 PTEST、C++の場合 TESTPtrというように接頭語、接尾語が付けられることが多い)
[](
C++は上記のような再定義はせず素直にクラスを定義し、③がよく使用されます。
C#も同様に値型を参照するために、ref キーワードが用意されています。
)
つまり、①~③の仮引数は十中八九、参照です。
intやdoubleのような値型は単純ですが、
結局のところ、TESTのようなオリジナルの型の場合、定義を調べないと
その仮引数が値として使われているか、参照として使われているかはわかりません。
##上記の関数に渡すときの型
C | C++ | C# | |
---|---|---|---|
① | [実体⇒実体] TEST test; hoge(test); or TEST *test; hoge(*test); ※ |
[実体⇒実体] TEST test; hoge(test); or TEST *test = new TEST(); hoge(*test); ※ |
[実体⇒参照] (クラスの場合) TEST test = new TEST(); hoge(test); |
② | [ポインタ⇒ポインタ] TEST *test; hoge(test) or TEST test; hoge(&test) |
[ポインタ⇒ポインタ] TEST *test = new TEST(); hoge(test) or TEST test; hoge(&test) |
- |
③ | - | [実体⇒参照] TEST test; hoge(test); or TEST *test = new TEST(); hoge(*test); |
- |
##あとがき
ポインタの概念をよく理解せず、オブジェクト指向のPython3やらC#をやって、C/C++をやると混乱します。
某漫画のように
「最初の入り口がオブジェクト指向なんだよね最近の子って」
「そうそう そんなんじゃ使いものにならねえっての」
となります。