cpp включает .cpp файлы в основной исходный файл вызывает ошибку «дубликата символа» - PullRequest
0 голосов
/ 28 августа 2018

Я застрял в том, как разделить проект на несколько исходных файлов и заголовочных файлов. Моя нынешняя практика кажется неуклюжей и ошибочной. Любой комментарий приветствуется!

У меня есть четыре файла:

  • main.cpp - основная программа. Он создаст несколько узлов дерева и вызовет функцию для их обхода.
  • TreeNode.h - это заголовочный файл, в котором я объявляю простой класс TreeNode
  • TreeNode.cpp - это место, где я определяю конструктор класса TreeNode
  • utils.cpp - это место, где я определяю несколько функций для TreeNode, например, распечатываем дерево.

Вопрос в том, где мне include файл TreeNode.h?

  • Если я включу оба в main.cpp и utils.cpp (так как они оба используют класс TreeNode, мой компилятор выдаст мне ошибку "duplicate symbol". Это может быть потому, что я включил utils.cpp в main.cpp а также.

Как это:

Scanning dependencies of target main
[ 25%] Building CXX object CMakeFiles/main.dir/main.cpp.o
[ 50%] Building CXX object CMakeFiles/main.dir/utils.cpp.o
[ 75%] Linking CXX executable main
duplicate symbol __Z13inorder_printP8TreeNode in:
    CMakeFiles/main.dir/main.cpp.o
    CMakeFiles/main.dir/utils.cpp.o
duplicate symbol __Z16inorderTraversalP8TreeNode in:
    CMakeFiles/main.dir/main.cpp.o
    CMakeFiles/main.dir/utils.cpp.o
ld: 2 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[3]: *** [main] Error 1
make[2]: *** [CMakeFiles/main.dir/all] Error 2
make[1]: *** [CMakeFiles/main.dir/rule] Error 2
make: *** [main] Error 2`
  • Если я включу только TreeNode.h в main.cpp файл, файл utils.cpp не будет скомпилирован. Выдает ошибку error: unknown type name 'TreeNode'

Редактировать:

Вот четыре файла:

main.cpp

#include <iostream>
#include <vector>
#include "TreeNode.h"
#include "utils.cpp"

using namespace std;

int main() {
    TreeNode * root = new TreeNode(0);
    root->right = new TreeNode(2);
    root->right->right = new TreeNode(3);

    // inorder_print(root);
    std::vector<int> v = inorderTraversal(root);

    // print out vector
    for (auto i = v.begin(); i != v.end(); ++i){
        std::cout << *i << ' ';
    }
    std::cout << std::endl;
    return 0;
}

TreeNode.h

#ifndef TREE_TREE_H
#define TREE_TREE_H

class TreeNode{
public:
    int val;
    TreeNode * left;
    TreeNode * right;

    TreeNode(int x);
};

#endif //TREE_TREE_H

TreeNode.cpp

#include "TreeNode.h"

TreeNode::TreeNode(int x) {
    val = x;
    left = nullptr;
    right = nullptr;
}

utils.cpp

#include <vector>
#include <iostream>
// #include "TreeNode.h"

// tested correct
void inorder_print(TreeNode * root){
    // print out the tree content in inorder traversal
    while(root != nullptr){
        std::cout << root->val << std::endl;
        inorder_print(root->left);
        inorder_print(root->right);
        break;
    }
}

std::vector<int> inorderTraversal(TreeNode * root){
    std::vector<int> v;
    while(root != NULL){
        v.push_back(root->val);
        if (root->left != NULL){
            v.insert(v.end(), inorderTraversal(root->left).begin(), inorderTraversal(root->left).end());
            break;
        }
        if (root->right != NULL){
            v.insert(v.end(), inorderTraversal(root->right).begin(), inorderTraversal(root->right).end());
            break;
        }
        break;
    }
    return v;
}

Ответы [ 2 ]

0 голосов
/ 28 августа 2018

, как показывает вывод вашего компилятора, вы компилируете main.cpp и utils.cpp. Ваш main.cpp включает utils.cpp, поэтому определенные вами функции компилируются дважды, поэтому дублируются символы.

Как правило, никогда не включайте файлы .cpp. Рассматривайте #include как буквальное включение содержимого одного файла в другой файл. Я полагаю, вы пытались решить проблему, из-за которой вы не могли использовать вещи из utils.cpp в mail.cpp. Чтобы решить эту проблему, добавьте функцию объявлений в TreeNode.h (например, просто сигнатуру функции, например, int foo(double);, сохраняя при этом их определение в utils.cpp (например, int foo(double) { return 0; })

0 голосов
/ 28 августа 2018

Вы можете использовать включить охранников. Например:

#ifndef TREENODE_H
#define TREENODE_H

class TreeNode 
{
    // stuff
};

#endif /* TREENODE_H */

Обратите внимание, что имя охранника (в данном случае TREENODE_H) должно быть уникальным для каждого заголовочного файла. И да, это может быть что угодно.

Отредактировано после редактирования вопроса:

Не включать utils.cpp. На самом деле, никогда не включайте *.cpp :), измените его на #include "utils.h". Это то, что вызывает ошибку компоновщика.

...