2

最近問題に遭遇しました。

Ah、B.cpp、C.cpp の 3 つのファイルがあります。

ああ

#ifndef __A_H__
#define __A_H__

int M()
{
    return 1;
}

#endif // __A_H__

B.cpp

#include "A.h"

C.cpp

#include "A.h"

MSVC で 3 つのファイルをコンパイルすると、エラーが発生します。

C.obj : error LNK2005: "int __cdecl M(void)" (?M@@YAHXZ) already defined in B.obj

ご存知のように、B.obj には "M" という名前のシンボルがあり、C.obj にも "M" という名前のシンボルがあります。ここでエラーが発生します。

ただし、以下のように M メソッドをメソッド M を含むクラスに変更すると、次のようになります。

ああ

#ifndef __A_H__
#define __A_H__

class CA
{
public:
    int M()
    {
        return 1;
    }
};

#endif // __A_H__

もうエラーはありません!! 誰かが何が起こっているのか教えてもらえますか?

4

3 に答える 3

7

B.cpp と C.cpp に Ah が含まれている場合、両方が の定義でコンパイルさMれるため、両方のオブジェクト ファイルにM. リンカーがすべての関数を収集すると、それMが 2 つのオブジェクト ファイルで定義されていることがわかり、どちらを使用すればよいかわかりません。したがって、リンカーは LNK2005 を発生させます。

関数Mをクラス宣言に入れると、コンパイラはMインライン関数としてマーク/処理します。この情報は、オブジェクト ファイルに書き込まれます。リンカは、両方のオブジェクト ファイルに のインラインバージョンの定義が含まれてCA::Mいることを認識し、両方が等しいと想定して、2 つの定義のいずれかをランダムに選択します。

もし書いていたら

class CA {
public:
    int M();
};

int CA::M()
{
    return 1;
}

これにより、最初のバージョンと同じ問題 (LNK2005) が発生しました。これCA::Mは、インライン化されていないためです。

ご想像のとおり、2 つの解決策があります。インライン化する場合はM、コードを次のように変更します

__inline int M()
{
    return 1;
}

インライン化を気にしない場合は、標準的な方法でインライン化を行い、関数宣言をヘッダー ファイルに入れます。

extern int M();

関数定義を cpp ファイルに入れます (Ah の場合、理想的には A.cpp になります)。

int M()
{
    return 1;
}

externヘッダー ファイルでは実際には必要ないことに注意してください。

別のユーザーがあなたの書き込みを提案しました

static int M()
{
    return 1;
}

これはお勧めしません。これは、コンパイラがM両方のオブジェクト ファイルMに配置し、各オブジェクト ファイル自体でのみ表示される関数としてマークすることを意味します。リンカは、B.cpp の関数が を呼び出しMていることMを確認すると、B.obj と C.obj を見つけます。どちらもM静的としてマークされているため、リンカーは C.obj を無視しM、B.objMから選択します。逆に、C.cpp の関数が を呼び出すM場合、リンカーはMC.obj から を選択します。M の定義が複数になり、すべて同じ実装になります。これはスペースの無駄です。

于 2013-04-11T07:28:25.470 に答える
0

フードの下にあるものはわかりませんが、クラスが必要ない場合は、コンパイラが関数に「extern」キーを自動的に追加するため、ヘッダーを含むエラーが 2 回発生します。

static キーワードを M() メソッドに追加すると、その関数のコピーがメモリ内に 1 つだけになり、コンパイル時にエラーが発生しなくなります。

ところで、#endif はありますが、#ifdef または #ifndef はありません。コピー/貼り付けエラーですか?

于 2013-04-11T06:51:48.893 に答える
0

http://faculty.cs.niu.edu/~mcmahon/CS241/c241man/node90.html ifdef ガードの方法を参照してください。定義の前に ifndef から始める必要があります。

編集:ああ、いや、あなたのガードが間違っている間、それは問題ではありません. 関数を機能させるには、関数の前に static を配置します。クラスは型を定義するため、異なります。

于 2013-04-11T06:13:09.467 に答える