私はC++を学ぼうとしているJava開発者ですが、標準的な関数宣言のベストプラクティスが何であるかはよくわかりません。
クラス内:
class Clazz
{
public:
void Fun1()
{
//do something
}
}
または外:
class Clazz
{
public:
void Fun1();
}
Clazz::Fun1(){
// Do something
}
私は2番目のものが読みにくくなることがあると感じています...
C++は、ソフトウェア開発のためのオブジェクト指向のパラダイムをサポートするという意味で、オブジェクト指向です。
ただし、Javaとは異なり、C++はクラス内で関数定義をグループ化することを強制しません。関数を宣言する標準C++の方法は、クラスなしで関数を宣言するだけです。
代わりにメソッドの宣言/定義について話している場合、標準的な方法は、宣言をインクルードファイル(通常は.h
または.hpp
という名前)に入れ、定義を別の実装ファイル(通常は.cpp
または.cxx
)。これは確かにやや面倒であり、いくつかの重複が必要ですが、言語の設計方法です。
簡単な実験と単一ファイルのプロジェクトでは何でも機能しますが、大規模なプロジェクトではこの分離は実際に必要なものです。
注:Javaを知っていたとしても、C++は完全に異なる言語です...そして、実験では習得できない言語です。理由は、多くの非対称性と明らかに非論理的な選択を備えたかなり複雑な言語であり、最も重要なことは、ミスを犯した場合、Javaのようにあなたを救う「ランタイムエラーエンジェル」がないことです...未定義の動作デーモン」。
C++を学ぶ唯一の合理的な方法は、読むことです...あなたがどれほど賢くても、委員会が決定したことを推測することはできません(正しい答えは非論理的であり、歴史的な結果であるため、実際に賢いことは時には問題ですらあります)遺産。)
良い本 または2つを選んで、それらを一から一まで読んでください。
最初は、メンバー関数をインライン関数として定義しますが、2番目は定義しません。この場合の関数の定義はヘッダー自体にあります。
2番目の実装では、関数の定義をcppファイルに配置します。
どちらも意味的に異なり、スタイルだけの問題ではありません。
関数定義はクラス外で行う方が適切です。そうすれば、必要に応じてコードを安全に保つことができます。ヘッダーファイルは宣言を提供するだけです。
誰かがあなたのコードを使いたいと思うなら、あなたは彼にあなたのクラスの.hファイルと.objファイル(コンパイル後に得られる)を与えることができます。彼はあなたのコードを使用するために.cppファイルを必要としません。
そうすれば、あなたの実装は誰にも見えません。
「Inside the class」(I)メソッドは「outside the class」(O)メソッドと同じです。
ただし、(I)は、クラスが1つのファイル(.cppファイル内)でのみ使用される場合に使用できます。 (O)は、ヘッダーファイルにある場合に使用されます。 cppファイルは常にコンパイルされます。 #include "header.h"を使用すると、ヘッダーファイルがコンパイルされます。
ヘッダーファイルで(I)を使用すると、#include "header.h"をインクルードするたびに関数(Fun1)が宣言されます。これにより、同じ関数を複数回宣言することになります。これはコンパイルが難しく、エラーにつながることさえあります。
正しい使用例:
File1:「Clazz.h」
//This file sets up the class with a prototype body.
class Clazz
{
public:
void Fun1();//This is a Fun1 Prototype.
};
File2:「Clazz.cpp」
#include "Clazz.h"
//this file gives Fun1() (prototyped in the header) a body once.
void Clazz::Fun1()
{
//Do stuff...
}
File3: "UseClazz.cpp"
#include "Clazz.h"
//This file uses Fun1() but does not care where Fun1 was given a body.
class MyClazz;
MyClazz.Fun1();//This does Fun1, as prototyped in the header.
File4:「AlsoUseClazz.cpp」
#include "Clazz.h"
//This file uses Fun1() but does not care where Fun1 was given a body.
class MyClazz2;
MyClazz2.Fun1();//This does Fun1, as prototyped in the header.
File5: "DoNotUseClazzHeader.cpp"
//here we do not include Clazz.h. So this is another scope.
class Clazz
{
public:
void Fun1()
{
//Do something else...
}
};
class MyClazz; //this is a totally different thing.
MyClazz.Fun1(); //this does something else.
メンバー関数は、クラス定義内で定義することも、スコープ解決演算子::を使用して個別に定義することもできます。クラス定義内でメンバー関数を定義すると、インライン指定子を使用しなくても、関数がインラインで宣言されます。したがって、以下のようにVolume()関数を定義できます。
class Box
{
public:
double length;
double breadth;
double height;
double getVolume(void)
{
return length * breadth * height;
}
};
必要に応じて、スコープ解決演算子を使用してクラス外で同じ関数を定義できます:::
double Box::getVolume(void)
{
return length * breadth * height;
}
ここで重要な点は、::演算子の直前にクラス名を使用する必要があることだけです。メンバー関数は、オブジェクトに対してドット演算子(。)を使用して呼び出され、そのオブジェクトに関連するデータを次のように操作します。
Box myBox;
myBox.getVolume();
(from: http://www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htm )、どちらの方法も有効です。
私は専門家ではありませんが、1つのファイルにクラス定義を1つだけ入れれば、それは実際には重要ではないと思います。
ただし、内部クラスのようなものを適用する場合、または複数のクラス定義がある場合、2番目の定義は読みにくく、維持しにくいでしょう。
最初のものは、ヘッダーファイル(クラスの宣言がある場所)に配置する必要があります。 2番目は、ヘッダーまたは通常はソースファイルのどこでもかまいません。実際には、クラス宣言に小さな関数を含めることができます(暗黙的にインラインで宣言しますが、最終的にインライン化するかどうかを決定するのはコンパイラです)。ただし、ほとんどの関数では、2番目の例のように、ヘッダーに宣言があり、cppファイルに実装があります。いえ、これが読みにくい理由はわかりません。言うまでもなく、実際にはいくつかのcppファイルに型の実装を分割できます。
クラス内で定義される関数は、デフォルトでインライン関数として扱われます。関数を外部で定義する必要がある簡単な理由:
クラスのコンストラクターは仮想関数をチェックし、適切なVTABLEまたは 仮想メソッドテーブル を指す仮想ポインターを初期化し、基本クラスコンストラクターを呼び出し、現在のクラスの変数を初期化します。いくつかの作業を行います。
インライン関数は、関数がそれほど複雑ではなく、関数呼び出しのオーバーヘッドを回避する場合に使用されます。 (オーバーヘッドには、ハードウェアレベルでのジャンプと分岐が含まれます。)また、上記のように、コンストラクターはインラインと見なされるほど単純ではありません。