クラスコードをヘッダーとcppファイルに分離する


169

単純なクラスの実装と宣言のコードを新しいヘッダーとcppファイルに分離する方法について混乱しています。たとえば、次のクラスのコードをどのように分離しますか?

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y)
  {
    gx = x;
    gy = y;
  }

  int getSum()
  {
    return gx + gy;
  }
};

12
ほんの2、3のコメント:コンストラクターは、本体にメンバーを設定する代わりに、常に初期化リストを使用する必要があります。良いと簡単な説明については、以下を参照してください。codeguru.com/forum/showthread.php?t=464084それは少なくともほとんどの場所で、通例は、上部にある公共の場を持つこともあります。これは何にも影響しませんが、パブリックフィールドはクラスのドキュメントであるため、それを先頭に置くことは理にかなっています。
martiert

2
@martiert ユーザーがこのアドバイスに従ってpublic:メンバーを移動した場合、メンバーを最上部に置くこと大きな影響を与える可能あります、メンバー間の順序依存関係があり、メンバーが宣言の順序で初期化されていることをまだ認識していませんでした;-)
underscore_d

1
確かに@underscore_d。しかし、繰り返しになりますが、エラーとしての警告と考えられるすべての警告をコンパイルしています。それは少なくともこれを
台無しに

@martiert良い点、警告を生成することをちょっと忘れました-ほとんどの人が警告のみを読んだ場合:-)私はそれらを使用して、それらをすべてコーディングしようとします。いくつかは避けられない-だから私は「警告のおかげで、私は私が何をしているか知っています!」-しかし、ほとんどは後で混乱を避けるために修正するのが最善です。
underscore_d

パブリックフィールドを上部に配置することは単なるスタイルであり、残念ながら、あまりにも多くの人が私の意見では採用しています。さらに、@ martiertが述べたように、いくつかのことを覚えておく必要があります。
Vassilis

回答:


232

クラス宣言はヘッダーファイルに入ります。#ifndefインクルードガードを追加することが重要です。MSプラットフォームを使用して#pragma onceいる場合は、も使用できます。また、私はプライベートを省略しました。デフォルトでは、C ++クラスメンバーはプライベートです。

// A2DD.h
#ifndef A2DD_H
#define A2DD_H

class A2DD
{
  int gx;
  int gy;

public:
  A2DD(int x,int y);
  int getSum();

};

#endif

そして実装はCPPファイルに行きます:

// A2DD.cpp
#include "A2DD.h"

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

53
テンプレートプログラミングを行っている場合は、すべてを.hファイルに保持して、コンパイラがコンパイル時に適切なコードをインスタンス化できるようにする必要があることに注意してください。
リネロ

2
#ifndefヘッダーにあるものはありますか?
Ferenc Deak

4
つまり、これは、ヘッダーファイルを含むすべてのファイルがプライベートメンバーを「参照」することを意味します。たとえば、libとそのヘッダーを公開する場合、クラスのプライベートメンバーを表示する必要がありますか?
Gauthier 2013年

1
いいえ、すばらしいプライベート実装イディオムがあります。en.wikipedia.org / wiki / Opaque_pointerこれを使用して、実装の詳細を非表示にすることができます。
Ferenc Deak 2013年

3
「クラス宣言はヘッダーファイルに挿入されます」という文言を含む小さな小刻み。これは確かに宣言ですが、これも定義ですが、後者には前者が含まれているため、クラス定義はヘッダーファイルに入れられると言いたいのです。翻訳単位には、クラスの定義ではなく、メンバー関数の定義があります。私は同意します、これは少し編集する価値がありますか?
lubgr

17

一般に、.hにはクラス定義が含まれています。これは、すべてのデータとすべてのメソッド宣言です。あなたの場合はこのように:

A2DD.h:

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y);    
  int getSum();
};

そして、.cppには次のようなメソッドの実装が含まれています。

A2DD.cpp:

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

7

プロジェクトをファイルに分割したいだけの場合、受け入れられた回答の手順は必要ないことを広義に調査する場合、この質問に出くわす読者に指摘することが重要です。単一クラスの複数の実装が必要な場合にのみ必要です。クラスごとの実装が1つの場合、それぞれのヘッダーファイルは1つで十分です。

したがって、受け入れられた回答の例から、この部分だけが必要です:

#ifndef MYHEADER_H
#define MYHEADER_H

//Class goes here, full declaration AND implementation

#endif

#ifndefなどのプリプロセッサ定義により、複数回使用できます。

PS。C / C ++が「ダム」であり、#includeが単に「このテキストをこの場所にダンプする」と言う方法に過ぎないことを理解すると、トピックはより明確になります。


「分割」ファイルをに置くことによってこれを行うことができますか.cpp、または.hこのコード編成の方法にとって本当に「良い」だけですか?
Benny Jobigan

1
一部のプロジェクトでは、ヘッダーファイルと(単一の)実装ファイルを分割して、実装のソースコードを明かさずにヘッダーファイルを簡単に配布できると考えました。
カールG

私が最初にC ++で学び、その後何年も前にC#に切り替えて、最近また多くのC ++を行っているので、これを指摘してくれてうれしいです。ヘッダー。私はこれを見つけたときにそれをしないという正当な理由を与える人を探して周りを探していました。@CarlGには良い点がありますが、そのシナリオ以外では、すべてインラインで行うのがよいと思います。
Peter Moore

6

基本的に、関数宣言/定義の変更された構文:

a2dd.h

class A2DD
{
private:
  int gx;
  int gy;

public:
  A2DD(int x,int y);

  int getSum();
};

a2dd.cpp

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

5

A2DD.h

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y);

  int getSum();
};

A2DD.cpp

  A2DD::A2DD(int x,int y)
  {
    gx = x;
    gy = y;
  }

  int A2DD::getSum()
  {
    return gx + gy;
  }

アイデアは、すべての関数のシグネチャとメンバーをヘッダーファイルに保持することです。
これにより、他のプロジェクトファイルは、実装を知らなくてもクラスがどのように見えるかを確認できます。

さらに、ヘッダーの代わりに他のヘッダーファイルを実装に含めることができます。ヘッダーファイルに含まれるどのヘッダーも、ヘッダーファイルを含む他のファイルに含まれる(継承される)ため、これは重要です。


4

宣言はヘッダーファイルに残します。

class A2DD
{
  private:
  int gx;
  int gy;

  public:
    A2DD(int x,int y); // leave the declarations here
    int getSum();
};

そして、定義を実装ファイルに入れます。

A2DD::A2DD(int x,int y) // prefix the definitions with the class name
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

あなたは2つを混ぜることができます(残す getSum()たとえば、ヘッダーに定義をます)。これは、たとえばコンパイラーがインライン展開する可能性が高くなるので便利です。ただし、実装を変更すると(ヘッダーに残っている場合)、ヘッダーを含む他のすべてのファイルの再構築がトリガーされる可能性があることも意味します。

テンプレートの場合、すべてをヘッダーに含める必要があることに注意してください。


1
プライベートメンバーと関数をヘッダーファイルに配置しても、実装の詳細の漏洩とは見なされませんか?
Jason

1
@ジェイソン、ちょっと。それらは必要な実装の詳細です。例えば、私はクラスがスタックでどれだけのスペースを消費するかを知らなければなりません。関数の実装は、他のコンパイル単位では必要ありません。
Paul Draper

1

通常、ヘッダーファイルには宣言と本当に短いインライン関数のみを入れます。

例えば:

class A {
 public:
  A(); // only declaration in the .h unless only a short initialization list is used.

  inline int GetA() const {
    return a_;
  }

  void DoSomethingCoplex(); // only declaration
  private:
   int a_;
 };

0

一般的な答えとしては非常に単純であるため、私はあなたの例も参照しません(たとえば、ヘッダーにそれらを実装することを強制するテンプレート関数が含まれていないなど)、経験則として従うのは、pimplです熟語

コンパイル時間が短縮され、構文上の砂糖が増えるため、いくつかの利点があります。

class->member の代わりに class.member

唯一の欠点は、あなたが支払う余分なポインターです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.