Edited: It seems there is a protocol for putting free functions in header files [1]
I've created a Visual Studio project to collect and organize some scrap code/examples that can be considered "related to polymorphism". I thought to do this by using namespaces to keep examples separate. Below, you can see I've defined a VirtualTables namespace, some classes within it, and a "free" function void Example3().
I thought to use header guards because VirtualTables.cpp already defines the namespace (and everything else inside) and I figured the line #include "VirtualTables.cpp" inside ex_Polymorphism.cpp would attempt to redefine the namespace (et al.). But header guards didn't fix the issue.
I get a linker error when building this project:
1>------ Build started: Project: ex_Polymorphism, Configuration: Debug Win32 ------
1>ex_Polymorphism.cpp
1>ex_Polymorphism.obj : error LNK2005: "void __cdecl VirtualTables::Example3(void)" (?Example3@VirtualTables@@YAXXZ) already defined in VirtualTables.obj
1>C:\Users\PC\Desktop\Code\C++\Scrapcode\ex_Polymorphism\Debug\ex_Polymorphism.exe : fatal error LNK1169: one or more multiply defined symbols found
1>Done building project "ex_Polymorphism.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
ex_Polymorphism.cpp
1 2 3 4 5 6 7 8 9 10 11 12
#include <iostream>
#include "VirtualTables.cpp"
int main()
{
VirtualTables::Example3();
int pauseConsole;
std::cin >> pauseConsole;
return 0;
}
inlinevoid Example3()
{
A a = B();
a.Function1(); // "A::Function1 Called!\n"
B b = B();
A *aptr = &b;
aptr->Function1(); // "B:Function1 Called!\n"
}
the problem goes away. Inlining [1] a function replaces everywhere it's called with the instructions inside that function, as if the function call never occurred at all (e.g., arguments and return values aren't pushed onto another call stack). But to the compiler, an inline function is still a function right? So why don't we have the same problem?
#include <iostream>
namespace VirtualTables
{
struct A
{
virtualvoid Function1()
{
std::cout << "A::Function1 Called!\n";
}
};
struct B : A
{
void Function1() override
{
std::cout << "B::Function1 Called!\n";
}
};
void Example3(); // Now just a declaration
}
/* Error if you try to provide implementation here
void VirtualTables::Example3()
{
A a = B();
a.Function1(); // "A::Function1 Called!\n"
B b = B();
A *aptr = &b;
aptr->Function1(); // "B:Function1 Called!\n"
}
*/
VirtualTables.cpp
1 2 3 4 5 6 7 8 9 10 11 12
#include <iostream>
#include "VirtualTables.hpp"
void VirtualTables::Example3()
{
A a = B();
a.Function1(); // "A::Function1 Called!\n"
B b = B();
A *aptr = &b;
aptr->Function1(); // "B:Function1 Called!\n"
}
#include "VirtualTables.hpp"
/* Will compile here
void VirtualTables::Example3()
{
A a = B();
a.Function1(); // "A::Function1 Called!\n"
B b = B();
A *aptr = &b;
aptr->Function1(); // "B:Function1 Called!\n"
}
*/
int main()
{
VirtualTables::Example3();
int pauseConsole;
std::cin >> pauseConsole;
return 0;
}
I'm a bit confused because [1] suggests that defining functions in a namespace should be possible.
There's another point [2] that doesn't seem to be true. Since this won't compile (here, I try to define the namespace in the VirtualTables.hpp/.cpp file.
#include <iostream>
#include "VirtualTables.hpp"
// We expect this declaration here to extend the declaration from VirtualTables.hpp
namespace VirtualTables
{
void Example4();
}
void VirtualTables::Example3()
{
A a = B();
a.Function1(); // "A::Function1 Called!\n"
B b = B();
A *aptr = &b;
aptr->Function1(); // "B:Function1 Called!\n"
Example4(); // This call is OK!
}
// This definition seems to be local to just this file
void VirtualTables::Example4()
{
std::cout << "Hello from Example4!\n";
}
ex_Polymorphism.cpp
1 2 3 4 5 6 7 8 9 10 11 12
#include <iostream>
#include "VirtualTables.hpp"
int main()
{
VirtualTables::Example3();
VirtualTables::Example4(); // Error: Namespace VirtualTables has no member "Example4"!
int pauseConsole;
std::cin >> pauseConsole;
return 0;
}
[1] C++ Primer 5'th Edition, Lippman, pp. 963 "18.2.1. Namespace Definitions"
A namespace definition begins with the keyword namespace followed by the namespace name. Following the namespace name is a sequence of declarations and definitions delimited by curly braces. Any declaration that can appear at global scope can be put into a namespace: classes, variables (with their initializations), functions (with their definitions), templates, and other namespaces: ...
[2] Ibid., pp. 964 "Namespaces Can be Discontinuous"
The fact that namespace definitions can be discontiguous lets us compose a
namespace from separate interface and implementation files. Thus, a namespace can
be organized in the same way that we manage our own class and function definitions:
• Namespace members that define classes, and declarations for the functions and
objects that are part of the class interface, can be put into header files. These
headers can be included by files that use those namespace members.
• The definitions of namespace members can be put in separate source files.
Organizing our namespaces this way also satisfies the requirement that various entities
—non-inline functions, static data members, variables, and so forth—may be defined
only once in a program.
One generally shouldn't put implementation code in a header file: each time the header is included somewhere, it breaks the One Definition Rule (ODR). Note that std::cout is implementation. Of course one can inlinea function, but only do this with trivial functions.
So the header file should contain function declarations, not function definitions. The definitions of functions go in the relevant cpp file.
With namespaces, in the header file put the whole class definition inside a namespace with braces, as you did in VirtualTables.hpp
In the cpp file use the namespace scope with each function definition, as in: