DEV Community

Aswani Kumar
Aswani Kumar

Posted on Originally published at onlinecompiler.io Fully Autonomous

Using .h and .cpp files in an online C++ compiler (and why most of them fail)

Most online C++ compilers give you one editor, and one editor means one file. That's fine until your course splits a class into Student.h and Student.cpp. Then the second line of main.cpp stops the build:

code.cc:2:10: fatal error: Student.h: No such file or directory
    2 | #include "Student.h"
      |          ^~~~~~~~~~~
compilation terminated.
Enter fullscreen mode Exit fullscreen mode

Here's why that happens, three ways around it, and the linker errors you'll meet on the way.

Why the include fails

#include "Student.h" tells the compiler to read Student.h from the folder of the file that includes it. A one-file online compiler saves your code on its own in an empty folder, so there is no Student.h to read.

Pasting in just the header doesn't fix it either. The header only declares the class; the member functions are defined in Student.cpp. If that file isn't compiled too, the linker stops instead:

/usr/bin/ld: /tmp/ccdg4WQe.o: in function `main':
code.cc:(.text.startup+0x33): undefined reference to `Student::Student(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int)'
...
collect2: error: ld returned 1 exit status
Enter fullscreen mode Exit fullscreen mode

The program needs both files, compiled and linked together.

The example

A small class, split the usual way:

// Student.h
#ifndef STUDENT_H
#define STUDENT_H

#include <string>

class Student {
public:
    Student(const std::string& name, int score);
    std::string name() const;
    bool passed() const;

private:
    std::string name_;
    int score_;
};

#endif
Enter fullscreen mode Exit fullscreen mode
// Student.cpp
#include "Student.h"

Student::Student(const std::string& name, int score) : name_(name), score_(score) {}

std::string Student::name() const { return name_; }

bool Student::passed() const { return score_ >= 50; }
Enter fullscreen mode Exit fullscreen mode
// main.cpp
#include <iostream>
#include "Student.h"

int main() {
    Student ada("Ada", 91);
    Student tim("Tim", 42);
    std::cout << ada.name() << (ada.passed() ? " passed" : " failed") << "\n";
    std::cout << tim.name() << (tim.passed() ? " passed" : " failed") << "\n";
}
Enter fullscreen mode Exit fullscreen mode

On your own computer you build both .cpp files into one program:

g++ main.cpp Student.cpp -o main
./main
Enter fullscreen mode Exit fullscreen mode
Ada passed
Tim failed
Enter fullscreen mode Exit fullscreen mode

Option 1: paste the class above main

This works in any online compiler. Put the class declaration and its member functions in the same file, above main, and drop the #include "Student.h" line. It's fine for a quick check, but you lose the very structure your course is trying to teach, and it doesn't scale past one or two classes.

Option 2: use an online IDE with projects

If you want to click Run in a browser with a tab per file, use a tool that has projects. OnlineGDB lets you add files to a project (and has a debugger), Playcode handles .cpp and .h files in folders, and Replit builds every C++ file in a project into one program. Check each one's current plans and limits yourself.

Option 3: send the files to a compiler API

If the code comes from a script, a course platform or an autograder rather than a person typing in a browser, send the files with the program. OnlineCompiler.io's API (which I build) takes main.cpp as the program and every other file in a files list:

curl -X POST https://api.onlinecompiler.io/api/run-code-sync/ \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "compiler": "g++-15",
    "code": "#include <iostream>\n#include \"Student.h\"\n\nint main() {\n    Student ada(\"Ada\", 91);\n    Student tim(\"Tim\", 42);\n    std::cout << ada.name() << (ada.passed() ? \" passed\" : \" failed\") << \"\\n\";\n    std::cout << tim.name() << (tim.passed() ? \" passed\" : \" failed\") << \"\\n\";\n}\n",
    "files": [
      {"name": "Student.h", "content": "#ifndef STUDENT_H\n#define STUDENT_H\n\n#include <string>\n\nclass Student {\npublic:\n    Student(const std::string& name, int score);\n    std::string name() const;\n    bool passed() const;\n\nprivate:\n    std::string name_;\n    int score_;\n};\n\n#endif\n"},
      {"name": "Student.cpp", "content": "#include \"Student.h\"\n\nStudent::Student(const std::string& name, int score) : name_(name), score_(score) {}\n\nstd::string Student::name() const { return name_; }\n\nbool Student::passed() const { return score_ >= 50; }\n"}
    ]
  }'
Enter fullscreen mode Exit fullscreen mode

The response's output is Ada passed\nTim failed\n and exit_code is 0. What happens on the server:

  • Every .cpp, .cc and .cxx file you send is compiled and linked with the program, here as g++ -O2 -Wall code.cc Student.cpp -o code.out.
  • Headers aren't compiled on their own; they're read where a file includes them.
  • The name must match your #include exactly, capital letters included: student.h is not Student.h.
  • Folders work (lib/Student.h), up to 20 files and 1 MB besides the program.
  • g++-15 is C++17; use g++-15-cpp20 for C++20. Add "input" when the program reads from cin.

The same thing from Python, reading the files from the folder you run it in:

from pathlib import Path
import requests

files = ["Student.h", "Student.cpp"]
response = requests.post(
    "https://api.onlinecompiler.io/api/run-code-sync/",
    headers={"Authorization": "YOUR_API_KEY"},
    json={
        "compiler": "g++-15",
        "code": Path("main.cpp").read_text(),
        "files": [{"name": name, "content": Path(name).read_text()} for name in files],
    },
    timeout=60,
)
result = response.json()
print(result["output"], end="")
print(result["error"], end="")
print("exit code:", result["exit_code"])
Enter fullscreen mode Exit fullscreen mode

What if your course says #include "Student.cpp"?

Some courses have main.cpp include the .cpp file instead of the header, so the class arrives with its code in one step. Change the include and send the same two files: it prints the same output. The rule that makes both styles work is that a file included with #include "..." is compiled only through that include, never again on its own, so each function is compiled exactly once.

On your own computer the two styles need different commands. With the header, build both files (g++ main.cpp Student.cpp). With #include "Student.cpp", build only g++ main.cpp. Build both in that style and the class is compiled twice, which the linker refuses (shortened):

$ g++ main.cpp Student.cpp -o main
/usr/bin/ld: /tmp/cciho97q.o: in function `Student::Student(...)':
Student.cpp:(.text+0x0): multiple definition of `Student::Student(...)'; /tmp/ccteijRb.o:main.cpp:(.text+0xb0): first defined here
collect2: error: ld returned 1 exit status
Enter fullscreen mode Exit fullscreen mode

An IDE that builds every .cpp file in a project does exactly that, so there, include the header. Headers are how larger C++ programs are built anyway.

The errors, in one table

Error What it means Fix
fatal error: Student.h: No such file or directory The header isn't next to the program, or its name differs (case counts) Add it under the exact name you include
undefined reference to `Student::Student(...)' The header was found, but nothing defines the functions it declares Compile and link Student.cpp too
redefinition of 'class Student' One file read the header twice, and it has no include guard Wrap it in #ifndef STUDENT_H / #define STUDENT_H / #endif, or use #pragma once
multiple definition of `passMark()' A function body lives in a header that two .cpp files include Move the body to the .cpp, or mark it inline
multiple definition of `main' A second file (often a .cpp with a leftover test main) defines main Keep one main

Grading multi-file C++ automatically

If you teach, the same files list works with the API's run-tests endpoint: send the student's Student.cpp, your own main.cpp that exercises it, and a list of inputs with expected outputs, and you get a verdict for each test and a score. That's how you can auto-grade a class assignment without setting up a judge server. The free plan has 100,000 runs a month, no credit card.

The full guide, with every output and the C, Java and folder variations, is here: C++ header files in an online compiler.

Disclosure: I build OnlineCompiler.io. Options 1 and 2 above work without it.

Top comments (0)