002e C++ tips

Category: soft

C++を忘れながら使っているので、 いろんなところで詰まります。

なので、 調べた結果なんかを少し書いておきます。

これじゃtipsじゃなくてただのメモだ……。

最終更新日時:2007年09月06日

もくじ

前置き

もっと上手なやり方を知っている人は、 黙ってスルーするか、 教えてください。

C++のenum

たとえばこんな enum を定義する。

namespace test
{
  enum Puzzle
  {
    Kumiki,
    RubiksCube,
    CastPuzzle
  };
  class Game
  {
    public:
      enum ActionPuzzle
      {
        Tetris,
        Panel_de_Pon,
        WreckingCrew98,
        Wario_no_mori,
        Kirby_no_kirakira_kids
      };
  };
  namespace Paper
  {
    enum PencilPuzzle
    {
      Slitherlink,
      Sudoku,
      Kakkuro
    };
  }
}

このようにアクセスする。

namespace test
{
  int appStart()
  {
    Puzzle              p  = Kumiki ;
    Game::ActionPuzzle  ap = Game::Panel_de_Pon ;
    Paper::PencilPuzzle pp = Paper::Kakkuro ;
    ……
  }
}

switch( enumを戻り値とする関数() ) としたらビルドが通らなかった事があるけど、なんでだろう……。

switch( enum ) は通ったのに。

リンクエラーの原因

ログファイル出力クラスを作る

ファイル出力を行いたいとき、 std::ofstream クラスを使います。

#include <fstream>  // std::ofstream, std::ios::app に必要(本当は奥深くにあるけど)
int main()
{
  // ファイルを開く
  std::ofstream fout( "test.txt", std::ios::app ); // test.txt を追記モードで開く
  // 開けなかった場合は終了
  if( fout.fail() )
  {
    return 1 ;
  }
  // 出力
  fout << "test" << std::endl ;
  // ファイルを閉じる
  fout.close();
  return 0 ;
}

おおまかな処理が終わるたびにファイルにログを出力したいとき、 ローカル変数として std::ofstream を使うと、 何度もファイルを開け閉めするはめになります。

合間の処理に時間がかかるならそれでも良いのですが、 連続で開け閉めするのはあまり気分が良くないかもしれません。

だから std::ofstream のインスタンスをメンバにもつファイル出力クラスを作って、 そいつに任せてしまえばいいわけです。

ここでは、 Output というクラスにしてみます。

// ここからヘッダ
#include <fstream>

// ファイル出力クラス
class Output
{
private:
  std::ofstream fout ;

public:
  Output( const char* const file ) throw( int ); // コンストラクタ
  void write( const char* const text );          // foutを使って出力を行う関数
};
// ここまでヘッダ。クラス定義の最後の ; ってよく忘れるんだよね……
#include <fstream>
Output::Output( const char* const file ) throw( int )
    : fout( file ) // ここでファイルを開く
{
  if( fout.fail() )
  {
    throw 1 ; // ファイルを開くのに失敗
  }
}

// 出力関数
void Output::write( const char* const text )
{
  fout << text << std::endl ; // 出力する
}

// 本体
int main()
{
  try
  {
    Output output( "test.txt" );
    output.write( "test" );
  }
  catch( int i ) // ファイルオープンに失敗したらここへ
  {
    return i ;
  }
  return 0 ;
}

さて、 ここでこの write() 関数を複数のクラスで使い回したくなるとします。 本来ログとはそういうもの。

というわけで、 write() を static にします。 static 関数からは、 static でないもの(引数やローカル変数をのぞく)を使う事はできないので、 fout も static にします。

とすると、 main() が始まる前に fout を初期化する必要があるので、 引数のないコンストラクタで初期化しておいて、 あとで open() 関数を使ってファイルを指定することにします。

// ここからヘッダファイル
#include <fstream>

// ファイル出力クラス
class Output
{
private:
  static std::ofstream fout ;

public:
  // コンストラクタは書かない
  // (特別な初期化をするクラス変数はないので、デフォルトコンストラクタで良い)
  static void open( const char* const file ) throw( int ); // 出力ファイル指定関数
  static void write( const char* const text ); // foutを使って出力を行う関数
};
// ここまでヘッダファイル

#include <fstream>

std::ofstream Output::fout ; // 静的変数 fout を引数のないコンストラクタで初期化

// 出力ファイル指定関数
void Output::open( const char* const file ) throw( int )
{
  Output::fout.open( file ); // ここでファイルを開く
  if( fout.fail() )
  {
    throw 1 ; // ファイルを開くのに失敗
  }
}

// 出力関数
void Output::write( const char* const text )
{
  fout << text << std::endl ; // 出力する
}

// 本体
int main()
{
  try
  {
    Output::open( "test.txt" );
    Output::write( "test" );
  }
  catch( int i ) // ファイルオープンに失敗したらここへ
  {
    return i ;
  }
  return 0 ;
}

という感じ。

あれ、 これどこでファイルを閉じるの? あとで考え直してみよう……