HOWTO · C++
Class Redefinition in C++
Understand and fix C++ class redefinition errors, then choose inheritance or composition to extend behavior.
On this page
A C++ class definition gives a name its layout and member declarations. Defining that class again in the same scope is ill-formed, so the compiler reports a class redefinition error. The fix is to find and remove or consolidate the duplicate definition. Inheritance and composition can help add behavior to a distinct type, but neither permits the original class to be defined twice.
Understand the C++ class redefinition error
A class definition introduces a type. Within one scope, a type name cannot be defined twice as a class, even if both definitions contain identical members. For example, two definitions of class DelftStack { }; in the same translation unit produce a diagnostic such as redefinition of 'class DelftStack'. Exact wording and source locations vary by compiler.
This differs from a forward declaration such as class DelftStack;, which announces a type without defining its members. A later full definition is allowed after that declaration. The error concerns repeated definitions, not every repeated mention of a class name.
Find duplicate definitions, including included headers
Start with the class name in the diagnostic and inspect the reported line and the earlier location the compiler identifies. Search the current source file for another definition. Then follow its #include directives: a header may define the same class, or two included headers may each provide a definition with that name.
A header included more than once can repeat its contents in a translation unit unless it uses an include guard or #pragma once. A conventional include guard is the portable preprocessor technique; #pragma once is a commonly supported compiler directive, but it is not part of the ISO C++ standard. Either technique prevents repeated inclusion of that same header; neither fixes two different headers that independently define the same class. Also check whether a source file both includes a defining header and repeats the class body itself.
Fix the error by keeping one definition
Choose one authoritative definition and remove the accidental copy. If both copies contain useful members, merge those members into the one definition, then update callers as needed. If a header owns the definition, include that header and avoid copying its class body into source files. Add a conventional include guard, or use the commonly supported #pragma once directive, to prevent the same header from being processed repeatedly.
Rename a class only when it represents a genuinely different type with a distinct role. Renaming an accidental duplicate can hide the underlying design or include problem and leave the program with two competing types. Before changing names, confirm that the program really needs both types and update their uses consistently.
Extend an existing class with inheritance
Use public inheritance when the new type is meaningfully a subtype of the base and should be usable through the base class’s public interface. A derived class can add members and functions; it does not redefine the base class. With public inheritance, public base members remain public and protected members remain available to derived-class code. Private base members are not directly accessible to the derived class. Private and protected inheritance change how base members are exposed to users of the derived type.
Run this self-contained example with a C++ compiler:
#include <iostream>
class Greeter {
public:
void greet() const { std::cout << "Hello from DelftStack!\n"; }
};
class FriendlyGreeter : public Greeter {
public:
void greet_twice() const {
greet();
greet();
}
};
int main() {
FriendlyGreeter greeter;
greeter.greet_twice();
}
Hello from DelftStack!
Hello from DelftStack!
This program writes the greeting twice. FriendlyGreeter is a specialized greeter and reuses the inherited public function. If that subtype relationship is not true, inheritance may make the interface misleading.
Add behavior through composition
Composition models a “has-a” relationship: one class stores an object of another class and delegates work to it. This keeps the contained type’s interface separate and lets the containing class choose which operations to expose. Here, Report stores a Formatter member and delegates rendering to that member; a report has a formatter, but it is not a formatter.
#include <string>
class Formatter {
public:
std::string format(const std::string& text) const {
return "Report: " + text;
}
};
class Report {
public:
std::string render(const std::string& text) const {
return formatter_.format(text);
}
private:
Formatter formatter_;
};
int main() {
Report report;
return report.render("Summary") == "Report: Summary" ? 0 : 1;
}
Composition also uses distinct class definitions. If a member object’s class is defined in a header, include that header and define the containing class once. Adding a member object does not resolve a duplicate definition elsewhere in the same scope.
Choose inheritance or composition for the change
Choose inheritance when the new type should satisfy the base type’s contract and can safely be used wherever the base is expected. Choose composition when the new type owns or uses another object as a component, or when it should expose only a selected part of that object’s behavior. Composition often keeps dependencies and public interfaces more independent; inheritance is useful when substitutability is intentional.
Neither approach is a correction for an accidental duplicate class definition. First establish one valid definition for each type. Then use inheritance or composition only if the desired design relationship calls for it.
Verify the fix with a compiler
After removing or consolidating the duplicate, compile the affected translation unit and any relevant project targets. For a single source file named main.cpp, a typical command is g++ -std=c++17 main.cpp -o app; run the resulting program with ./app. The inheritance example above was compiled and run: it exited with status 0 and wrote the two greeting lines shown above. The duplicate-definition diagnostic is illustrative; the supplied execution evidence covers the inheritance example, not a run of the diagnostic case.
If the diagnostic remains, inspect the compiler’s two reported locations and trace included headers again. If the duplicate appears only in one build configuration, compare its include paths and preprocessor definitions. Keep the diagnostic text selectable when sharing it, since its wording and line numbers depend on the compiler and build.