あの頃のC++と、もはややらないこと
今回は、1990年ごろ当時は当たり前にやっていたけど、今はどうやらそうではないらしいということがらをいくつかピックアップし、紹介します。次回からは、Modern C++のことをいろいろ紹介していきますが、そのための地ならしみたいな感じでお付き合いください。
1. newもdeleteも呼ばないで!
- new演算子もdelete演算子も使わない。
- インスタンスの生成はmake_unique関数とmake_shared関数で。
1990年ごろにC++を始めた人にとって、C言語にはなかったいろんな構文を使うのがうれしくもあり、誇らしくもありました。何しろ、Javaが1990年代中盤ごろに登場するまでは、C++が唯一の実用的なオブジェクト指向プログラミングが可能な言語プラットフォームでしたから。C言語でゴリゴリ書いていた、構造体を使うコードをクラスに置き換え、メモリ確保と初期化を同時に行えるなど、意識してC++の世界に浸ろうとしていたものです。
リスト1を見てください。なんてことないC言語のコードです。構造体を定義し、その領域をヒープ上に確保、フィールドを初期化して出力し、使い終わったら解放するというコードです。C言語のコメントにダブルスラッシュが使えるようになったのはC99からなので、ここではあえて古いスタイルとしています。
#include "stdio.h" #include "stdlib.h" /* 構造体を定義する */ struct MyStructure { int id; char name[16]; }; int main() { /* 構造体のメモリを確保する */ struct MyStructure *s = (struct MyStructure *)malloc(sizeof(struct MyStructure)); /* 構造体のメンバを初期化する */ s->id = 1; s->name[0] = 'A'; /* 構造体のメンバを出力する */ printf("%d: %c\n", s->id, s->name[0]); /* 構造体のメモリを解放する */ free(s); return 0; }
C++の世界に移すにあたり、こんな構造体があったら、これをわざわざクラスにして、コンストラクタを作って初期化もクラス任せにしようということをするわけです。C++では、ダブルスラッシュによるコメントが使えました(リスト2)。
#include <iostream> using namespace std; // クラスを定義する class MyClass { public: MyClass(); // デフォルトコンストラクタ int id; char name[16]; }; // デフォルトコンストラクタ MyClass::MyClass() { // メンバを初期化する id = 0; name[0] = '\0'; } int main() { // クラスのインスタンスを生成する MyClass *c = new MyClass(); // クラスのメンバを出力する c->id = 1; c->name[0] = 'A'; cout << c->id << c->name[0] << "\n"; // インスタンスを解放する delete c; return 0; }
ここではMyClassというクラスを定義しています。メンバは、先ほどの構造体と同じです。簡略化のために、MyClassクラスの全てのメンバはpublicであることをお許しください。publicにしたことで、全てのメンバはどこからでもアクセス可能、すなわち可視となります。C++のクラスでは、publicのようなアクセス制御子を記述しないと、デフォルトで全てのメンバはprivateすなわち不可視となります。
メンバには、ほかにデフォルトコンストラクタがあります。これは、インスタンスの生成時にデフォルトで呼び出されるメソッドです。デフォルトコンストラクタには引数どころか戻り値もありません。なお、引数を与えて初期化の方法をカスタマイズできる、コンストラクタのオーバーロードを定義することができます。
さて、クラスのインスタンスを動的に作成するときには、このようにnew演算子を使ってコンストラクタを呼び出し、使い終わったらdelete演算子でインスタンスを解放する、これがC++の流儀であり、長い間それだけだと思い込んでいました。何しろ、後発のJavaでもnew演算子を使ってインスタンスを生成するということが、Java 19になっても行われているのですから(Javaの場合は、インスタンスのお片付けは自動になっています)。
このnew演算子とdelete演算子の組み合わせは、形が変わっただけでC言語におけるmalloc関数とfree関数の対応そのままです。newしたのにdeleteしなければメモリリークの原因になりますし、newしてないのにdeleteしようとしたらダングリングポインタを産み出してしまいます。やはり、このようなことはよくないということで、Modern C++ではmake_unique関数というものを使って安全にメモリの確保と解放が行えるようになっているらしいのです。この関数については第3回あたりで紹介できるのではないかと思います。