1. ホーム
  2. c++

[解決済み】C++のforward宣言とは何ですか?

2022-04-05 12:44:42

質問

で。 http://www.learncpp.com/cpp-tutorial/19-header-files/

を記載しています。

add.cpp:

int add(int x, int y)
{
    return x + y;
}

main.cppです。

#include <iostream>

int add(int x, int y); // forward declaration using function prototype

int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
    return 0;
}

コンパイラが " が何であるかを知ることができるように、前方宣言を使用しました。 add をコンパイルするとき、" は main.cpp . 先に述べたように、別のファイルにある関数を使いたいたびに前方宣言を書くのは、すぐに面倒になります。

"の説明をお願いします。 前方宣言 さらに?で使用すると何が問題なのでしょうか? main() 関数を使用できますか?

どのように解決するのですか?

C++でforward-declareが必要な理由

コンパイラは、あなたがスペルミスをしたり、関数に渡す引数の数を間違えたりしていないことを確かめたいのです。そのため、コンパイラは'add'(あるいは他の型、クラス、関数)を使用する前に、まずその宣言を確認するように要求している。

これにより、コンパイラはコードの検証をより適切に行うことができ、未解決の部分を整理して、見た目がきれいなオブジェクトファイルを生成できるようになります。もし、前方宣言する必要がなかったら、コンパイラはオブジェクトファイルを生成する際に、その中に関数 add である可能性があります。そしてリンカーは、どの add を呼び出そうとしたとき、実際に呼び出したのは add 関数が別のオブジェクトファイル内にある場合、リンカは add を使って dll または exe . リンカが間違った add . 例えば、あなたが int add(int a, float b) しかし、リンカはすでに存在する int add(int a, int b) で、それが正しいものだと思い、代わりにそれを使っています。あなたのコードはコンパイルされますが、あなたが期待していたようなことはできません。

そのため、コンパイラは、物事を明確にし、推測などを避けるために、使用する前にすべてを宣言することを義務づけています。

宣言と定義の違い

余談だが、宣言と定義の違いを知っておくことは重要である。 関数の場合は、戻り値の型、呼び出し方法、メソッド名、引数、およびその型がこれにあたります。しかし、メソッドのコードは必要ありません。定義には、宣言と、関数のコードも必要です。

フォワード宣言でビルド時間を大幅に短縮する方法

ある関数の宣言を、現在の .cpp または .h ファイルを作成する際に、すでに関数の宣言が含まれているヘッダを#includeすることで、その関数を使用することができます。しかし、これはコンパイルを遅くする可能性があります。 #include ヘッダを .h の代わりに .cpp を #include しているものはすべて、あなたのプログラムの .h を書くと、あなたが書いたすべてのヘッダーも#includeされてしまうのです。突然、1つか2つの関数を使いたいだけなのに、コンパイラは何ページものコードを#includeしてしまい、コンパイルする必要があるのです。これを避けるには、前方宣言を使用して、ファイルの先頭で関数の宣言を自分で入力すればいいのです。数個の関数を使うだけなら、ヘッダを常に#includeするのに比べて、コンパイルが本当に速くなる。大規模なプロジェクトでは、1時間以上のコンパイル時間を数分に短縮できるかもしれません。

2つの定義が互いに利用しあうような循環的な参照を解除する。

さらに、前方宣言はサイクルを断ち切るのに役立ちます。これは、2つの関数が互いに利用しあおうとしている状態です。このような場合(これは完全に正しい行為です)、次のようにします。 #include というヘッダーファイルがありますが、そのヘッダーファイルは #include そのヘッダがもう一つのヘッダを#includeし、そのヘッダが今書いているヘッダを#includeしています。それぞれのヘッダーファイルが他のヘッダーを再インクルードしようとする、鶏と卵のような状況に陥ってしまうのです。これを解決するには、必要な部分を一方のファイルで前方排他的に宣言し、そのファイルから#includeを除外すればいいのです。

ファイル Car.h

#include "Wheel.h"  // Include Wheel's definition so it can be used in Car.
#include <vector>

class Car
{
    std::vector<Wheel> wheels;
};

ファイル Wheel.h

ふむふむ・・・宣言の Car が必要です。 Wheel へのポインタを持っています。 Car が、しかし Car.h は、コンパイラーエラーになるため、ここに含めることはできません。もし Car.h をインクルードした場合、その後に Wheel.h を含むことになります。 Car.h が含まれます。 Wheel.h このままでは永遠に続くので、コンパイラはエラーを発生させます。解決策は、前方に Car の代わりに

class Car;     // forward declaration

class Wheel
{
    Car* car;
};

もしクラス Wheel のメソッドを呼び出す必要があるメソッドを持っていました。 Car で定義されている場合、それらのメソッドは Wheel.cppWheel.cpp を含めることができるようになりました。 Car.h を使用すると、サイクルが発生しません。