web-dev-qa-db-ja.com

関数の複数の定義に関するエラー

数年前にイントロコースを受講した後、C++を再学習しようとしていますが、基本的な問題がいくつかあります。フレンド機能を使用しようとすると、現在の問題が発生します。これが私のファイルの2つのファイルです。

最初:

// fun.cpp

#include <iostream>
using namespace std;

class classA {
    friend void funct();
public:
    classA(int a=1,int b=2):propa(a),propb(b){cout<<"constructor\n";}
private:
    int propa;
    int propb;
    void outfun(){
        cout<<"propa="<<propa<<endl<<"propb="<<propb<<endl;
    }
};
void funct(){                     // ERROR HERE
    cout<<"enter funct"<<endl;
    classA tmp(1,2);
    tmp.outfun();
    cout<<"exit funct"<<endl;
}

第二:

// mainfile.cpp
#include <iostream>
#include "fun.cpp"
using namespace std;

int main(int nargin,char* varargin[]) {
    cout<<"call funct"<<endl;
    funct();
    cout<<"exit main"<<endl;
    return 0;
}

私が得ているエラーは「 `funct() 'の複数の定義」です。フレンド関数として宣言するときに間違った構文を使用していますか?

43
Adad Dayos

以下は、C++でコードをビルドしたときに何が起こるかについて、非常に単純化されていますが、適切なビューです。

C++は、マシンの実行可能コードを生成する負荷を次の異なるフェーズに分割します-

  1. 前処理-これは、マクロ-#definesなどを使用している場合に展開される場所です。

  2. コンパイル-各cppファイルと、そのファイル内のすべての#includedファイル(直接または間接的に(コンパイルユニットと呼ばれる)が、機械可読オブジェクトコードに変換されます。

    ここで、C++は、定義されているすべての関数(つまり、{}に本体が含まれていること、たとえばvoid Foo( int x){ return Boo(x); })が有効な方法で他の関数を参照していることも確認します。

    それを行う方法は、呼び出す前に少なくともこれらの他の関数(例えばvoid Boo(int);)の宣言を提供するように主張することです。そうすることで、とりわけ適切に呼び出していることを確認できます。これは、呼び出されるcppファイルで直接実行するか、通常はインクルードヘッダーファイルで実行できます。

    このcppおよびインクルードファイルで定義された関数に対応するマシンコードのみが、このコンパイルユニット(例:Foo)のオブジェクト(バイナリ)バージョンとしてビルドされ、単に宣言されたもの(例:Boo)ではないことに注意してください。

  3. リンク-これは、C++が各コンパイルユニットで宣言および呼び出されたものを探し、それが呼び出される場所にリンクする段階です。この関数の定義が見つからなかった場合、リンカーはあきらめてエラーを出します。同様に、同じ関数シグネチャ(本質的にはそれが取る名前とパラメーターのタイプ)の複数の定義を見つけた場合、それをあいまいであると見なし、任意に1つを選択したくないとエラーになります。

後者はあなたの場合に起こっていることです。 #includeファイルのfun.cppを実行すると、fun.cppmainfile.cppの両方がfunct()の定義を持ち、リンカーはプログラムでどちらを使用するかを知らず、それについて文句を言っています。

上記のVaughnによる修正は、mainfile.cppfunct()の定義を含むcppファイルを含めず、代わりにfunct()の宣言を別のヘッダーファイルに移動して、mainline.cppに含めることです。このようにして、コンパイラーはfunct()の宣言を処理し、リンカーはfun.cppからfunct()の定義を1つだけ取得し、自信を持って使用します。

59
Mohit Chugh

問題は、プログラムの2つの場所にfun.cppを含めると、それを2回定義することになり、有効ではないことです。

cppファイルを含めたくありません。ヘッダーファイルを含める必要があります。

ヘッダーファイルにはクラス定義が必要です。対応するcppファイル(個別にコンパイルします)には、関数定義が含まれます。

fun.hpp:

#include <iostream>

class classA {
    friend void funct();
public:
    classA(int a=1,int b=2):propa(a),propb(b){std::cout<<"constructor\n";}
private:
    int propa;
    int propb;
    void outfun(){
        std::cout<<"propa="<<propa<<endl<<"propb="<<propb<< std::endl;
    }
};

fun.cpp:

#include "fun.hpp"

using namespace std;

void funct(){
    cout<<"enter funct"<<endl;
    classA tmp(1,2);
    tmp.outfun();
    cout<<"exit funct"<<endl;
}

mainfile.cpp:

#include <iostream>
#include "fun.hpp"
using namespace std;

int main(int nargin,char* varargin[]) {
    cout<<"call funct"<<endl;
    funct();
    cout<<"exit main"<<endl;
    return 0;
}

通常、ヘッダーファイルでusing namespace stdを使用しないことをお勧めします。

26
Vaughn Cato