Подтвердить что ты не робот

Ошибка с несколькими определениями функции

Я пытаюсь выучить С++ после того, как несколько лет назад начал курс интро, и у меня возникли некоторые основные проблемы. Моя текущая проблема возникает при попытке использовать функцию друга. Вот мой код в 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() ". Я использую неправильный синтаксис при объявлении его как функции друга?

4b9b3361

Ответ 1

Вот очень упрощенный, но, надеюсь, релевантный взгляд на то, что происходит, когда вы создаете свой код на С++.

С++ разбивает нагрузку исполняемого кода генерирующей машины на следующие разные фазы -

  • Предварительная обработка. Здесь будут использоваться все макросы - #defines и т.д., которые вы можете использовать.

  • Компиляция. Каждый файл cpp вместе со всеми файлами #include d в этом файле, прямо или косвенно (вместе называемый модулем компиляции), преобразуется в машиночитаемый объектный код.

    Здесь С++ также проверяет, что все функции определены (т.е. содержат тело в { }, например,
    void Foo( int x){ return Boo(x); }) справедливо ссылаются на другие функции.

    Как это делается, настаивая на том, что вы предоставляете хотя бы объявление этих других функций (например, void Boo(int);), прежде чем вы его вызываете, чтобы он мог проверить, что вы правильно его называете. Это можно сделать либо непосредственно в файле cpp, где он вызывается, либо обычно во включенном заголовочном файле.

    Обратите внимание, что только машинный код, соответствующий функциям, определенным в этом файле cpp и включенным, создается как двоичная версия этой единицы компиляции (например, Foo), а не те, которые просто объявлены (например, Boo).

  • Связывание - это этап, на котором С++ отправляется на охоту за объявленными и вызываемыми в каждом компиляторе элементами и связывает их с местами, где он вызван. Теперь, если не было определения, найденного в этой функции, компоновщик отказывается и ошибки. Точно так же, если он находит несколько определений одной и той же сигнатуры функции (по сути, имя и типы параметров, которые она принимает), она также выдает ошибку, поскольку она считает ее неоднозначной и не хочет выбирать один произвольно.

Последнее - это то, что происходит в вашем случае. Делая #include файла fun.cpp, оба fun.cpp и mainfile.cpp имеют определение funct(), и компоновщик не знает, какой из них использовать в вашей программе, и жалуется на это.

Исправление, упомянутое выше, заключается в том, чтобы не включать файл cpp с определением funct() в mainfile.cpp и вместо этого переместить объявление funct() в отдельный файл заголовка и включить его в mainline.cpp. Таким образом, компилятор получит объявление funct(), и компоновщик получит только одно определение funct() от fun.cpp и будет использовать его с уверенностью.

Ответ 2

Проблема в том, что если вы включите fun.cpp в два места в вашей программе, вы в конечном итоге определите его дважды, что недействительно.

Вы не хотите включать файлы 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 в файлах заголовков.