HOWTO · C++

C++ の Pragma Once

この記事では、C++ で一度使用したプラグマの基本、プログラマーに役立つ方法と理由について説明します。

この記事は、プリプロセッサディレクティブとしての pragma once の概要と、その使用方法の簡単な例から始まります。この後、C および C++ でのサポートに関する pragma once のいくつかの特定のプロパティについて説明し、最後に、その使用方法と代替手段よりも優れている方法のより包括的な例を示します。

C++ の Pragma Once

pragma once は、C および C++ でプリプロセッサディレクティブとして使用されます。プリプロセッサディレクティブは、プログラムを構成せず、代わりにプリプロセッサによって読み取られる 1 行のコードです。

プリプロセッサは、コードをコンパイルする前にこれらを分析します。プリプロセッサは、これらのディレクティブの最後にセミコロンを期待していません。

したがって、pragma once ディレクティブをコードに追加するには、ファイルの先頭に以下を追加します。

#pragma once

pragma once 自体は非標準です。これは、一部のコンパイラがそれをサポートしないことを意味し、その複雑な実装のために、常に信頼できるとは限りません。

ただし、これは非常に一般的にサポートされています。したがって、ほとんどの場合、プラグマを 1 回使用しても問題はありません。その目的は単純です。現在のソースファイル(ディレクティブが書き込まれているファイル)がコンパイルに 1 回だけ含まれるようにします。

この作業は、include guardsを使用して代わりに実行できます。pragma once の使用方法の例の後で、違いについて説明します。

pragma once は、主にクラスを定義するときに使用されます。次の例では、StructureRoom の 2つのクラスを定義します。

Room クラスは Structure クラスから継承されます。

// This contains the code for the Structure class
// Save this as Structure.h
#pragma once

#include <iostream>
#include <string>

using namespace std;

class Structure {
  string struc_name;
  int id;

 public:
  void setVals() {
    struc_name = "Example Name";
    id = 1;
  }

  void printVals() {
    cout << "Name is: " << struc_name << " and id is: " << id << endl;
  }
};

次に、Room クラスを定義します。

// Save this as Room.h
#pragma once

#include "Structure.h"

class Room {
  Structure example;

 public:
  void initRoom() { example.setVals(); }

  void getRoom() { example.printVals(); }
};

これで、上記のオブジェクトを使用する main 関数を定義できます。

// save this in a main.cpp file
#include <iostream>

#include "Room.h"
#include "Structure.h"

using namespace std;

int main() {
  Room testRoom;

  testRoom.initRoom();
  testRoom.getRoom();

  return 0;
}

上記の例では、コードを実行すると、出力として Name is: Example Name and id is: 1 が出力されます。これは期待どおりです。ただし、プラグマを 1 回削除すると、次のエラーが発生します。

In file included from Room.h:3,
                 from main.cpp:2:
Structure.h:8:7: error: redefinition of 'class Structure'
    8 | class Structure
      |       ^~~~~~~~~
In file included from main.cpp:1:
Structure.h:8:7: note: previous definition of 'class Structure'
    8 | class Structure
      |       ^~~~~~~~~

クラス Structure は 2 回定義されています。pragma once を使用すると、1 回だけ読み取られるようにすることができます。

別の方法は、代わりに識別子を使用することです。上記の例のコンテキストでは、以下に定義されているようにそれを行うことができます。

#ifndef STRUCTURE
#define STRUCTURE

class Structure {
  // Some sample code here
};
#endif

上記の例では、STRUCTURE は何にでも置き換えることができます。ただし、通常、クラスの名前に対応する名前を保持します。

ただし、ここで問題が発生する可能性があります。第一に、それはより冗長であり、手動の意思決定を必要とし、コンパイラーは、プロジェクト内のどこかでマクロ名を誤って使用するプログラマーからの保護がありません。

コンパイラは通常、コンパイル速度を上げるために pragma once 用に最適化されています。