C・C++でconstを正しく使う
このレッスンでは、キーワードconstで読み取り専用のデータを宣言し、ポインタとconstの組み合わせ方の違いを理解します。
はじめに
キーワードconstを使うと、関数は受け取ったデータを変更しないと約束できます。constの付いたデータを誰かがうっかり変更すると、コンパイラがすぐにエラーを出します。これでコードが安全になり、読む人もどの関数がデータを変更できるのかがわかりやすくなります。
大切なポイント
- ポインタは、まだ存在している正しい型のオブジェクトを指すか、nullにしておきましょう。
- 確保したメモリ領域は、ちょうど1回だけ解放しましょう。
- どの関数がメモリの持ち主(ownership)で、どのデータが読み取り専用(const)かをはっきりさせましょう。
CとC++のコード例
C
main.c
#include <stdio.h>
int sum(const int *values, size_t count) {
int total = 0;
for (size_t i = 0; i < count; ++i) total += values[i];
return total;
}
int main(void) {
const int values[] = {2, 4, 6};
printf("%d\n", sum(values, 3));
return 0;
}
12
C++
main.cpp
#include <array>
#include <iostream>
int sum(const std::array<int, 3>& values) {
int total = 0;
for (int value : values) total += value;
return total;
}
int main() {
const std::array values{2, 4, 6};
std::cout << sum(values) << '\n';
}
12
CとC++の比較
引数const int *valuesは、関数sumが配列の要素を読むだけで変更しないことを示します。宣言は変数名から外側へ読むとわかりやすいです。constデータへのポインタ(const int *)は指している値を守り、constポインタ(int * const)は別のアドレスを指せません。C++にはさらにconstメンバー関数があり、オブジェクトの状態を変えないことを約束します。
C
メモリの持ち主と解放は、すべてプログラマーが自分で保証します。
C++
コンテナとRAIIが、オブジェクトの寿命が終わるときにリソースを自動で解放します。
発展問題
両方のバージョンを実行し、コードを書き換えて、それぞれの言語がもたらす安全性の違いを観察しましょう。
- nullポインタやメモリ確保の失敗で試しましょう。
- AddressSanitizerとUndefinedBehaviorSanitizerを付けて実行し、メモリのエラーを見つけましょう。
- どのポインタがメモリの持ち主で、どのポインタが読むだけかをはっきり書き添えましょう。