2

SFML ライブラリを使用して C++ 開発に頭を悩ませようとしています。チュートリアル ( http://www.gamefromscratch.com/page/Game-From-Scratch-CPP-Edition-Part-7.aspx ) に従い、Visual Studio 2010 を使用しています。

未解決の外観に関して私が常に直面している問題。私が遭遇するほとんどのエラーとは異なり、a) コードとは関係がなく、b) 一貫した動作をしていないように見えるため、私はこれに本当に苦労しています。皆さんに具体的な例を挙げてその 1 つの例を解決するための支援を求めるのではなく、これらの問題に対処するためのより信頼できる方法を開発したいと考えています。ただし、一般的な発生の概要を説明します。

8 つのヘッダー ファイルとそれらに対応する 8 つの cpp ファイルを使用したソリューションがあります。ソリューションは安定しています。エラーや警告なしでコンパイルおよび実行されます。

ヘッダー ファイルに移動して、次の行を追加します。

仮想ボイド DoNothing();

次に、一致する cpp ファイルに移動し、メソッドを記述します。

void DoNothing(){};

コンパイルして実行すると、5 つの未解決の外部エラーが発生します。それらはコードのどの行も指していないので、それらを修正する方法はよくわかりませんが、明らかに何か間違ったことをしました. けっこうだ。安定した状態に戻ろうとして、挿入した 2 行のコードを削除してコンパイルします。コードは最後の安定状態と同じですが、同じ未解決の外部エラーが発生します。

ランダムなことを試して、別の cpp ファイルに移動し、含まれている 2 つのヘッダー ファイルの順序を逆にします。ゲームがコンパイルされます。インクルードされたヘッダー ファイルの順序を元に戻すと、コンパイルされます。

未解決の外部エラーとは何ですか? 入力したコードと一貫して動作しないように見えるのはなぜですか? 問題が何であるかを知るためにそれらを読むにはどうすればよいですか? また、そもそもそれらを回避するにはどうすればよいでしょうか?

ありがとうございました。

ps: 提供すべき具体的な詳細がある場合は、お知らせください。

4

2 に答える 2

1

「未解決の外部」エラーは、コードが存在しないもの (通常は関数またはメソッドですが、変数の場合もあります) を参照していることを意味します。これらはリンクエラーであり、コンパイルエラーではありません。そのため、行番号やより役立つエラー メッセージが表示されません。

C++ コードを実行可能ファイルに変換する方法について少し背景を説明します (少し単純化していることに注意してください)。

プロジェクト内の各 C++ ソース ファイル (ヘッダー ファイルではない) は、個別にコンパイルされます。「.cpp」ファイルとそれに含まれるすべてのヘッダーは、オブジェクト ファイルまたはオブジェクト コードと呼ばれるものにコンパイルされます。(これらのファイルの拡張子は「.obj」または「.o」です。) ライブラリ ファイル (Windows では「.lib」ファイル、Linux では「.a」ファイル) は、これらのオブジェクト ファイルの集合と考えることができます。 、後で使用するために保存されます。

実行可能プログラム (Windows の EXE または DLL ファイルなど) を作成するには、これらのオブジェクト ファイルをすべてリンクます

ここで重要なことは、各ソース ファイルが独立してコンパイルさ、他のソース ファイルから独立していることです。そのため、あるファイルのコードが別のファイルに実装されている関数を呼び出す場合、コンパイラはその関数の実際の本体を認識せず、呼び出された関数の宣言が可視である限り(つまり、プロトタイプ、つまりヘッダーに書いた行)、これらのファイルは最終的に一緒にリンクされ、実際にリンカーを呼び出すタスクを残します。これは通常、適切なヘッダーをインクルードしている限り、コンパイラが満足することを意味します。

しかし、リンカーはより粘り強く、衒学的になります。リンク時には、プロジェクト全体で使用するすべての関数の本体 (つまり、実装) を提供する必要があります。すべての適切なオブジェクト ファイルとライブラリが一緒にリンクされ、使用されている各関数の実装がそれらの間のどこかに 1 回だけ存在することを確認するのはあなたの仕事です。

これにより、問題が発生します。「未解決の外部」リンカー エラーが発生した場合、これは、呼び出した関数の本体が、リンクしているオブジェクト ファイルとライブラリのどこにも存在しないことを意味します。

明らかに、2 つのうちの 1 つが起こっています。外部ライブラリのヘッダーをインクルードしたが、ライブラリ ファイル自体にリンクするのを忘れた (これはここでは問題ではありません) か、関数を宣言 (つまり、プロトタイプを作成) したが、その本体を実装するのを忘れました。 .

ここでは、リンカーが非常に厳密であることに注意してください。クラスで次のように宣言すると、次のようになります。

class Foo {
    void bar (int x);
};

次に、「.cpp」ファイルで、次の関数を実装します。

void bar (int x)
{
    // Do nothing
}

Foo::bar()実際にプログラムのどこかで呼び出すと、未解決の外部エラーが発生します。これは、実装されbar()たメソッドが実装されていないためですFoo(実装する必要がありvoid Foo::bar (int x) {}ます)。わずかにスペルミスをしたり、引数の型を間違ったり、その他のことをしたりすると、同様のことが起こります。 .

リンカのエラーを読んで意味を理解するの難しい場合があります。場合によっては、リンカが文句を言っている名前 (見つけられないと言っている「シンボル」) がすべて認識できないほど壊れていることがあります。これは、*Application Binary Interface* (ABI) と、数十年の歴史と先例に関係しています。とにかく、ほとんどの場合、リンク エラー メッセージをよく見ると、関数名が何であるかを確認し、コード (またはライブラリ) をチェックして、もう一度やり直してください。

また、まれではありますが、リンクの問題を解決するために、プロジェクトを完全に再構築する必要がある場合があります。

于 2013-04-14T19:45:02.960 に答える
0

このような動作を見るたびに、プロジェクト間の循環参照が原因でした。たとえば、プロジェクト A にはプロジェクト B に実装されたオブジェクト/シンボルへの参照があり、同時にプロジェクト B にはプロジェクト A のオブジェクト/シンボルへの参照があります。ソリューションをビルドするたびに、ツールは 1 つのプロジェクトをコンパイルする必要があります。最初に、次に他の。コンパイルする 2 番目のプロジェクトに変更を加えると、最初のプロジェクトはコンパイルの最初のラウンドで変更を認識できず、ビルドは失敗します。プロジェクト B を (現在は廃止されたライブラリ B のコピーに対して) 手動でビルドできた場合、ソリューションは正しくビルドを開始します。より複雑なサイクルも可能です (たとえば、A は B に依存し、B は B に依存し、B は C に依存し、B は A に依存します)。複数のプロジェクトについて明示的に言及していませんが、それらを持っているに違いありません。

これらの循環参照は、長い間存在し、時間の経過とともにゆっくりと成長してきた大規模なソリューションで一般的です。ここからは 1 つの関数、そこからは構造体が必要なため、人々はあらゆるものからあらゆるものへのリンクを追加する習慣を身につけます。

これらの依存関係を探し出します。ソース コードだけから完全にクリーンなリビルドを実行できるはずです。依存関係ツリーは次のようになります...まあ、ツリーです。グラフではありません。

于 2013-04-14T22:28:37.160 に答える