-1

I am stuck at how to separate a project into few source files and header files. My current practice seems to be clumsy and mistaken. Any comment is appreciated!

I have four files:

  • main.cpp is the main program. It will create a few tree nodes, and call a function to traverse them.
  • TreeNode.h is the header file where I declare a simple class TreeNode
  • TreeNode.cpp is where I define a constructor of the class TreeNode
  • utils.cpp is where I define a few functions on TreeNode, like printing out the tree.

The question is, where should I include the TreeNode.h file?

  • If I include it both in main.cpp and utils.cpp (since they both use the TreeNode class, my compiler gives me a "duplicate symbol" error. This might be because I included utils.cpp in main.cpp as well.

Like this :

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`
  • If I only include TreeNode.h in main.cpp file, the utils.cpp file will not compile. It gives an error error: unknown type name 'TreeNode'

Edit:

Here are the four files:

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;
}
yuqli
  • 4,461
  • 8
  • 26
  • 46

2 Answers2

2

as your compiler output shows, you're compiling both main.cpp and utils.cpp. Your main.cpp includes utils.cpp so functions you defined there are compiled twice hence duplicate symbols.

As the rule of thumb, never include .cpp files. Consider #include as literal inclusion of content of one file into another file. I suppose you tried to resolve the issue that you couldn't use stuff from utils.cpp in mail.cpp. To solve this add function declarations in TreeNode.h (like just a signature of a function, e.g. int foo(double); while keeping their definition in utils.cpp (e.g. int foo(double) { return 0; })

Andriy Tylychko
  • 15,967
  • 6
  • 64
  • 112
0

You can use include guards. For instance:

#ifndef TREENODE_H
#define TREENODE_H

class TreeNode 
{
    // stuff
};

#endif /* TREENODE_H */

Note that the guard name (in this case, TREENODE_H) has to be unique for each header file. And yes, it can be whatever you want.

Edited after question edit:

Don't include utils.cpp. In fact, never include a *.cpp:) change it to #include "utils.h". This is what is causing that linker error.

Ricardo Costeira
  • 3,171
  • 2
  • 23
  • 23