C++編で扱っている C++ は 2003年に登場した C++03
という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14
-> C++17 -> C++20 -> C++23 と更新されています。
なかでも C++11 での更新は非常に大きなものであり、これから C++
の学習を始めるのなら、C++11
よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい
C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。
問題① 次のプログラムの誤りを指摘してください。
#include <iostream>
int main()
{
static const int ARRAY_SIZE = 10;
int* array = new int[ARRAY_SIZE];
for (int i = 0; i < ARRAY_SIZE; ++i) {
[i] = i;
array}
for (int i = 0; i < ARRAY_SIZE; ++i) {
std::cout << array[i] << std::endl;
}
delete array;
}
new演算子が配列版でありながら、delete演算子が非配列版を使われています。正しくは、
delete [] array;
としなければいけません。
問題② デストラクタを利用すれば、delete演算子の呼び忘れを防げます。new演算子で確保された MyClass型のポインタを対象に、そのような呼び忘れを防ぐためのクラスを設計してください。
まず、MyClassクラスを定義します。
// MyClass.h
#ifndef MYCLASS_H_INCLUDED
#define MYCLASS_H_INCLUDED
class MyClass {
public:
(const char* s);
MyClass~MyClass();
private:
const char* mStr;
};
#endif
// MyClass.cpp
#include "MyClass.h"
::MyClass(const char* s) : mStr(s)
MyClass{
std::cout << "MyClass(" << mStr << ")" << std::endl;
}
::~MyClass()
MyClass{
std::cout << "~MyClass(" << mStr << ")" << std::endl;
}
ここで一度、main側のプログラムを書いてみます。まず、MyClassクラスしかないとして、普通に書くと次のようになります。
// main.cpp
#include "MyClass.h"
* func()
MyClass{
* p2 = new MyClass("c2");
MyClass* p3 = new MyClass("c3");
MyClass
delete p3;
return p2; // p2 を解放するのは呼び出し元
}
int main()
{
* p1 = new MyClass("c1");
MyClass* p2 = func();
MyClass
delete p1;
delete p2; // main関数内で new しているわけではない p2 の delete を忘れないようにできるか?
}
実行結果:
MyClass(c1)
MyClass(c2)
MyClass(c3)
~MyClass(c3)
~MyClass(c1)
~MyClass(c2)
new をした場所と、delete をする場所とが離れている場合は特にそうなのですが、忘れずに delete を行うことが困難になることがあります。この練習問題の意図は、このような場合でも決して delete を忘れないようにする手を探すことにあります。
そこで、新たなクラスを作成して、delete を行う責任をそのクラスに一任します。そのクラスの名称を MyClassPtrクラスとすると、次のように書けるようにしたいのです。
// main.cpp
#include "MyClass.h"
* func()
MyClass{
* p2 = new MyClass("c2");
MyClass(new MyClass("c3")); // delete は MyClassPtr に任せる
MyClassPtr p3
return p2; // p2 を解放するのは呼び出し元
} // p3 が破棄されるとき、管理下にある MyClass の delete を行う
int main()
{
(new MyClass("c1"));
MyClassPtr p1(func());
MyClassPtr p2
} // p1、p2 が破棄されるとき、管理下にある MyClass の delete を行う
MyClassPtrクラスのオブジェクトは、new を使わずにインスタンス化していますから、関数を抜け出すときに破棄されます。このとき呼び出されるデストラクタの中で、管理下にある MyClass を delete してやれば、うまく自動化できそうです。
MyClassPtr を定義してみます。
// MyClassPtr.h
#ifndef MYCLASSPTR_H_INCLUDED
#define MYCLASSPTR_H_INCLUDED
class MyClassPtr {
public:
(MyClass* ptr);
MyClassPtr~MyClassPtr();
private:
* mPtr;
MyClass};
#endif
// MyClassPtr.cpp
#include "MyClassPtr.h"
::MyClassPtr(MyClass* ptr) :
MyClassPtr(ptr)
mPtr{
}
::~MyClassPtr()
MyClassPtr{
delete mPtr;
}
コンストラクタで、MyClass型のポインタを受け取り、メンバ変数^に保持しておきます。あとは、デストラクタで delete してやるだけです。こうしておけば、もはや delete を呼ぶことに気を使う必要はありません。
【上級】まさにこういった目的で使える std::auto_ptrクラステンプレートが、標準ライブラリに用意されています(【標準ライブラリ】第16章)。
ちなみに、デストラクタの実装ですが、本編で登場した SAFE_DELETEマクロを使って次のように書くことは可能ですが、これは少し無駄ではあります。
::~MyClassPtr()
MyClassPtr{
(mPtr);
SAFE_DELETE}
ここはデストラクタなので、ここから抜け出してしまえば、もはや mPtr にアクセスする手段はありません。そのため、解放の後にヌルポインタで上書きする行為には大して意味がありません。
全体的に見直し修正。
新規作成。
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |