この章の概要です。
前の章でも触れましたが、C++ では関数の仮引数の void は省略できます。
#include <iostream>
void hello()
{
std::cout << "Hello" << std::endl;
}
int main()
{
();
hello}
実行結果:
Hello
C++ では、仮引数が空になっている場合は、C言語で void と指定したときと同じ意味、すなわち引数はないという意味です。void と明示的に書いても同じ意味になりますが、C++ では面倒で無意味な入力を避けて、void は省略することが一般的です。
C言語では、仮引数が空であることは、「引数は何でも良い」という意味です(C言語編第10章)。これは関数プロトタイプとして機能しないことを意味しており、一般的にいって危険な使い方です。ですから、C言語では必ず void と明示するべきです。
なお、「void」という記述を、意味を変えずに省略できるのは、仮引数の場合だけです。戻り値の型指定の省略は、C言語と同様に、int型を指定したことになります。
C言語(C95以前) では、関数定義の際に戻り値の型の指定を省略すると、int を指定したものとみなされますが、C++ では、関数定義の際に戻り値の型の指定を省略できません。
C言語でも、C99以降は省略できなくなっています。
#include <iostream>
() // コンパイルエラー
hello{
std::cout << "Hello" << std::endl;
}
int main()
{
();
hello}
文字リテラルは、C言語では int型ですが(C言語編第19章参照)、C++ では char型です。そのため、大きさも異なります。
#include <iostream>
int main()
{
char a = 'A';
std::cout << sizeof('A') << "\n"
<< sizeof(a) << std::endl;
}
実行結果:
1
1
【上級】このルール変更は、関数のオーバーロード(第10章)をうまく行うために必要だからです。‘A’ が 65 という int型とみなされてしまうのでは不便になってしまいます。
ところで、上のプログラムで、std::cout を使って整数が出力できていることが分かると思いますが、このように、型の判断までも自動的に行ってくれることが、printf関数と決定的に異なるところです。
C言語の文字列リテラルは char の配列型ですが、C++ では const な char の配列型です。そのため、C言語でよく行われていた char* の変数へ代入する行為はコンパイルできません。
#include <iostream>
int main()
{
char* str = "abcde"; // コンパイルエラー
std::cout << str << std::endl;
}
実際に、先ほどのサンプルプログラムを試すと、Visual Studio 2017、clang 5.0.0 のいずれでも、コンパイルできてしまいます。しかし、正しく動作する保証はありません。
上記のように、言語の違いや、コンパイルの実情から混乱がありますが、変数で受け取る必要があるときは、つねに const を付けるように徹底しておけば間違いはありません。
C言語では、構造体タグを使って構造体変数を宣言する際、structキーワードが必要です(C言語編第26章参照)が、C++ では省略できるようになりました。
struct Point2D {
int x;
int y;
};
int main()
{
;
Point2D point
// 以下、省略
}
上のプログラムで、main関数の中で構造体変数 point を宣言する際に、structキーワードがありません。C言語であれば、
; struct Point2D point
このように書かないといけません。
なお、このルールは、enum(C言語編第50章参照)や union(C言語編第55章参照)でも同様です。
C言語では、void*型から任意のポインタ型へ暗黙的に変換できますが、C++ では明示的なキャストを必要とします。逆に、任意のポインタ型から void*型への変換は、C言語でも C++ でも暗黙的に行えます。
#include <iostream>
#include <cstdlib>
int main()
{
// キャストが必要
int* p = (int*)std::malloc(sizeof(int));
*p = 100;
std::cout << *p << std::endl;
std::free(p);
}
実行結果:
100
malloc関数の戻り値は void*型です。C言語でこの関数を使う際、明示的にキャストせずとも、非void*型の変数で受け取れますが、C++ ではキャストが必要です。
実際には、C++ では、このサンプルプログラムのような用途で、malloc関数を使うことはなく、通常は、new演算子を使います(第15章)。
キャストに関しても、C++ では新しい構文が追加されており(第9章)、それを使う方が良いのですが、C言語と同じ構文でも目的は果たせます。
任意のポインタ型から void*型への変換は、C言語でも C++ でも暗黙的に行えます。
C言語では、整数値を、列挙型の変数へ代入できますが、C++ では明示的なキャストを必要とします。逆に、列挙型の値から、整数型への変換は、C言語でも C++ でも暗黙的に行えます。
#include <iostream>
int main()
{
enum Color {
,
RED,
GREEN,
BLUE};
= (Color)1; // キャストが必要
Color color int v = BLUE; // キャスト不要
std::cout << color << "\n"
<< v << std::endl;
}
実行結果:
1
2
グローバルな const
は、C言語では外部結合されますが、C++
では内部結合されます。
外部結合は、ある名前が、定義を行っている場所以外の翻訳単位(他のソースファイル)からでも使えることを意味し、
内部結合は使えないことを意味します。
C++ で、外部結合の constグローバル変数を作りたいときには、extern を付加するようにします。 また、extern を付加しない場合、必ず初期値を与える必要があります。
外部結合の定数を定義したいときには、ヘッダファイルに extern付きの定義を公開するようにします。そして、どこかのソースファイルで1つだけ、extern無し・初期値付きの定義を書きます。このとき、公開したヘッダファイルを先にインクルードして、extern付きの定義が見えるようにします。
たとえば、次のような構成になります。
// data.cpp
#include "data.h"
const int DATA_VALUE = 100;
// data.h
#ifndef DATA_H_INCLUDED
#define DATA_H_INCLUDED
extern const int DATA_VALUE;
#endif
// main.cpp
#include <iostream>
#include "data.h"
int main()
{
std::cout << DATA_VALUE << std::endl;
}
実行結果:
100
C言語には、もはや使い道のない古いキーワード auto がありますが、C++ では、新たな意味が与えられています。
C++ の auto の意味については、第19章であらためて解説します。
C言語にある register指定子(C言語編第57章)は、C++ では使用が推奨されません。
問題① 次のプログラムは、文字と整数のどちらを出力しますか?
#include <iostream>
int main()
{
std::cout << 'A' << std::endl;
}
問題② 次のプログラムを、C言語でも C++ でもコンパイルでき、同じ実行結果になるように書き換えてください。
#include <iostream>
enum Color {
,
RED,
GREEN
BLUE};
int main()
{
= 0;
Color color
+= 1;
color
std::cout << color << std::endl;
}
C++編の更新に合わせて、修正を加えた。
「VisualC++」という表現を「VisualStudio」に統一。
「サイズ」という表記について表現を統一。 型のサイズ(バイト数)を表しているところは「大きさ」、要素数を表しているところは「要素数」。
Xcode 8.3.3 を clang 5.0.0 に置き換え。
clang 3.7 (Xcode 7.3) を、Xcode 8.3.3 に置き換え。
新規作成。
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |