クラス内外の関数宣言


91

私はC ++を学ぼうとしているJAVA開発者ですが、標準の関数宣言のベストプラクティスが何であるかはよくわかりません。

クラスで:

class Clazz
{
 public:
    void Fun1()
    {
        //do something
    }
}

または外:

class Clazz
{
public:
    void Fun1();
}

Clazz::Fun1(){
    // Do something
}

2番目のものは読みにくくなる可能性があると感じています...


1
ここには実際には3つのオプションがあります。2番目の例では、ヘッダーファイル(ただしインライン化されていない)または別の.cppファイルに関数定義を含めることができます。
コーディグレイ

この質問はあなたが理解するのを助けるかもしれません。
ビョルンPollex

3
注:宣言は常にクラスの内部にありますが、定義は内部または外部のいずれかにあります。質問のタイトルと本文はs /宣言/定義に従う必要があります/私を信じていないのですか?stackoverflow.com/q/1410563/1143274
Evgeni Sergeev 2015

1
クラス内の関数定義は避ける必要があります。それらは暗黙的に見なされinlineます。
John Strood 2016年

@JohnStroodそう?inline別の翻訳ユニットが使用する場合に必要な1つの定義規則のみを緩和しますClazz
Caleth

回答:


57

C ++は、ソフトウェア開発のオブジェクト指向パラダイムをサポートするという意味で、オブジェクト指向です。

ただし、Javaとは異なり、C ++では関数定義をクラスにグループ化する必要はありません。関数を宣言するための標準的なC ++の方法は、クラスなしで関数を宣言することです。

代わりに、メソッドの宣言/定義について話している場合、標準的な方法は、宣言だけをインクルードファイル(通常は.horという名前.hpp)に入れ、定義を別の実装ファイル(通常は.cpporという名前.cxx)に入れることです。私はこれが確かにやや煩わしく、いくらかの複製を必要とすることに同意しますが、それは言語がどのように設計されたかです。

迅速な実験や単一ファイルプロジェクトの場合は何でも機能しますが、大規模なプロジェクトの場合、この分離は実際に必要なものです。

注:Javaを知っている場合でも、C ++は完全に異なる言語です...そしてそれは実験では習得できない言語です。その理由は、それが多くの非対称性と明らかに非論理的な選択を伴うかなり複雑な言語であり、そして最も重要なことに、間違いを犯したときにJavaのようにあなたを救うための「ランタイムエラーエンジェル」がないからです...しかし代わりに「未定義の振る舞いデーモン」。

C ++を学ぶ唯一の合理的な方法は、読むことです...あなたがどれほど賢くても、委員会が何を決定したかを推測する方法はありません(正解は非論理的で歴史的な結果であるため、実際には賢いことは問題になることさえあります遺産。)

良い本を1、2選んで読んでください。


7
誰かがJavaから来て、C ++で助けを求めた場合、「あなたが知っている言語は何かに夢中になっている」と言ったら、それは彼に何を伝えますか?彼は他の言語と比較していないので、これは彼にほとんど何も伝えません。OPにあまり伝えない、取りつかれているような感情的に意味のある言葉を使用するよりも、この部分を省くことを検討してください。さらに、「すべてにクラスを使用する」という文脈は何ですか?Javaでは、メソッドにクラスを使用しません。変数にクラスを使用しません。ファイルにクラスを使用しません。では、ここでの「すべて」とは何ですか?暴言?
ダニエルS.

3
@DanielS:どうやらあなたを怒らせたので、その部分を削除しました(理由はわかりません)。確かに、私は実際にはJavaをまったく使用していないので、Javaについては怒りません。当時、オブジェクト指向プログラミングとしてのOOPは面白い冗談だと思っていましたが、明らかにそうではありませんでした。私はJava1.1認定プログラマーでしたが、その当時、何らかの理由で強制されない限り、その「プログラミング言語」を使用しないことを決定し、これまでのところそれを回避することに成功しました。
6502

おかげで、私はそれが今ずっと良く読めると思います。気分を害してすみません。次回はもっと前向きになりたいと思います。
ダニエルS.

15
質問に答えません
Petr Peller 2015年

1
@PetrPeller:3番目の段落ではっきりしない部分は何ですか?
6502 2015年

27

1つ目は、メンバー関数をインライン関数として定義しますが、2つ目は定義しません。この場合の関数の定義は、ヘッダー自体にあります。

2番目の実装では、関数の定義をcppファイルに配置します。

どちらも意味的に異なり、スタイルの問題だけではありません。


2
cplusplus.com/doc/tutorial/classesは同じ答えを出します:「クラスメンバー関数をそのクラス内に完全に定義することと、プロトタイプと後でその定義のみを含めることの唯一の違いは、最初の場合、関数は自動的にコンパイラによってインラインメンバー関数と見なされましたが、2番目のクラスメンバー関数は通常の(インラインではない)クラスメンバー関数であり、実際には動作に違いはないと想定しています。」
buttons840 2013

18

関数定義はクラス外の方が優れています。そうすれば、必要に応じてコードを安全に保つことができます。ヘッダーファイルは宣言を与えるだけです。

誰かがあなたのコードを使いたいと思ったら、あなたは彼にあなたのクラスの.hファイルと.objファイル(コンパイル後に得られる)を与えることができます。彼はあなたのコードを使用するために.cppファイルを必要としません。

そうすれば、あなたの実装は他の誰にも見えなくなります。


10

「クラス内」(I)メソッドは、「クラス外」(O)メソッドと同じように機能します。

ただし、(I)は、クラスが1つのファイル(.cppファイル内)でのみ使用される場合に使用できます。(O)は、ヘッダーファイル内にある場合に使用されます。cppファイルは常にコンパイルされます。#include "header.h"を使用すると、ヘッダーファイルがコンパイルされます。

ヘッダーファイルで(I)を使用する場合、関数(Fun1)は、#include "header.h"をインクルードするたびに宣言されます。これにより、同じ関数を複数回宣言する可能性があります。これはコンパイルが難しく、エラーにつながることさえあります。

正しい使用例:

ファイル1:「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. 

つまりClazz MyClazzClazz MyClazz2
chupo_cro

4

メンバー関数は、クラス定義内で定義することも、スコープ解決演算子::を使用して個別に定義することもできます。インライン指定子を使用しない場合でも、クラス定義内でメンバー関数を定義すると、関数がインラインで宣言されます。したがって、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();  

http://www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htmから)、どちらの方法も合法です。

私は専門家ではありませんが、1つのファイルに1つのクラス定義だけを入れれば、それは実際には問題ではないと思います。

ただし、内部クラスのようなものを適用する場合、または複数のクラス定義がある場合、2番目のクラスは読みにくく維持されません。


1
そのリンクから関連するコンテンツを投稿の本文に取り込んで、リンク切れに対する将来性を保証できますか?ありがとう
JustinJDavies 2014

2

最初のものは、ヘッダーファイル(クラスの宣言が存在する場所)に配置する必要があります。2つ目は、ヘッダーまたは通常はソースファイルのいずれかです。実際には、小さな関数をクラス宣言に入れることができます(これは暗黙的にインラインで宣言しますが、最終的にインライン化するかどうかを決定するのはコンパイラーです)。ただし、ほとんどの関数では、2番目の例のように、ヘッダーに宣言があり、cppファイルに実装があります。いいえ、これが読みにくくなる理由はわかりません。言うまでもなく、実際には、型の実装を複数のcppファイルに分割することができます。


1

クラス内で定義された関数は、デフォルトでインライン関数として扱われます。関数を外部で定義する必要がある単純な理由:

クラスのコンストラクターは、仮想関数をチェックし、適切なVTABLEまたは仮想メソッドテーブルを指すように仮想ポインターを初期化し、基本クラスコンストラクターを呼び出し、現在のクラスの変数を初期化するため、実際にいくつかの作業を行います。

インライン関数は、関数がそれほど複雑ではなく、関数呼び出しのオーバーヘッドを回避する場合に使用されます。(オーバーヘッドには、ハードウェアレベルでのジャンプと分岐が含まれます。)そして、上記のように、コンストラクターはインラインと見なされるほど単純ではありません。


「インライン」は、実質的にインライン化とは何の関係もありません。インラインで定義されたメンバー関数が暗黙的にインラインで宣言されるという事実は、ODR違反を回避するためにあります。
ビッグテンプ

0

インライン関数(クラスで宣言したときの関数)を呼び出すたびに、メインのメモリコードに貼り付けられます。クラスの外で関数を宣言するとき、関数を呼び出すとき、それは同じメモリから来ます。それがはるかに優れている理由です。

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