動的なオブジェクトの生成 解答ページ | Programming Place Plus Modern C++編【言語解説】 第15章

トップページModern C++編 C++編](../../index.html) – 第15章

Modern C++編は作りかけで、更新が停止しています。代わりに、C++14 をベースにして、その他の方針についても見直しを行った、新C++編を作成しています。
Modern C++編は削除される予定です。

問題①

問題① 「RAII」の項のサンプルプログラムで、MyClassWrapperクラスは MyClass のオブジェクトを保持していますが「公開」されていないので、事実上使用できません。たとえば、次のような関数があると、MyClassWrapperクラスが保持している MyClass のオブジェクトが必要になります。

void f(const MyClass* mc);

MyClassWrapperクラスのオブジェクトを使いつつも、上記のような関数が利用できるように、MyClassWrapperクラスにメンバを補ってください。


単純な方法は、保持しているポインタ変数を返す「公開」されたメンバ関数を追加することです。

#include <iostream>

class MyClass {
public:
    MyClass()
    {
        std::cout << "Constructor" << std::endl;
    }
    ~MyClass()
    {
        std::cout << "Destructor" << std::endl;
    }
};

class MyClassWrapper {
public:
    MyClassWrapper() :
        mObj(new MyClass())
    {}

    ~MyClassWrapper()
    {
        delete mObj;
    }

    inline MyClass* Get()
    {
        return mObj;
    }

private:
    MyClass* const  mObj;
};

void f(const MyClass* mc)
{
    std::cout << "f()" << std::endl;
}

int main()
{
    MyClassWrapper obj;
    f(obj.Get());
}

実行結果:

Constructor
f()
Destructor

「非公開」のポインタ変数を「公開」してしまうことに怖さもありますが、単なるラッパークラスであると考えれば、そこまで厳格に考えなくても良いかもしれません。ただ、それを受け入れたとしても、使う際には「.Get()」のような余分な表記が必要になるため、やや不便ではあります。

もう1つの方法は、型変換演算子(第9章)を定義することです。

#include <iostream>

class MyClass {
public:
    MyClass()
    {
        std::cout << "Constructor" << std::endl;
    }
    ~MyClass()
    {
        std::cout << "Destructor" << std::endl;
    }
};

class MyClassWrapper {
public:
    MyClassWrapper() :
        mObj(new MyClass())
    {}

    ~MyClassWrapper()
    {
        delete mObj;
    }

    inline operator MyClass*()
    {
        return mObj;
    }

private:
    MyClass* const  mObj;
};

void f(const MyClass* mc)
{
    std::cout << "f()" << std::endl;
}

int main()
{
    MyClassWrapper obj;
    f(obj);
}

実行結果:

Constructor
f()
Destructor

この方法では利便性は高まりますが、意図せず起こる型変換が怖いところです。

いずれにしても、この例のように、delete演算子の呼び出しを自動化することが目的であれば、スマートポインタを使った方が良いでしょう。

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass()
    {
        std::cout << "Constructor" << std::endl;
    }
    ~MyClass()
    {
        std::cout << "Destructor" << std::endl;
    }
};

void f(const MyClass* mc)
{
    std::cout << "f()" << std::endl;
}

int main()
{
    std::unique_ptr<MyClass> obj(new MyClass());
    f(obj.get());
}

実行結果:

Constructor
f()
Destructor

問題②

問題② RAII の考え方を使って、std::fopen関数に対する std::fclose関数を確実に行うクラスを設計してください。


コンストラクタ(他のメンバ関数でも構いません)でファイルをオープンして、デストラクタでクローズするようにします。std::FILE のポインタは、メンバ変数として保持しておきます。

#include <cstdio>
#include <iostream>

class MyClass {
public:
    MyClass(const char* fname)
    {
        mFp = std::fopen(fname, "r");
    }

    ~MyClass()
    {
        std::fclose(mFp);
    }

private:
    std::FILE*  mFp;
};

int main()
{
    MyClass obj("test.txt");
}

実行結果:

本来は、std::fopen関数を使わず、std::fstream などのファイルストリームを使うのが良いです。ファイルストリームについては、【標準ライブラリ】第13章で解説します。


参考リンク


更新履歴

’2017/9/9 新規作成。



第15章のメインページへ

Modern C++編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る