Why I can implement the class's member function in header file? - c++

I know that you should only declare a function in header and avoid define it because if more than one source file include this header ,the linker will tell you there are duplicate symbol.
I also know that it is recommended that declare a class in header and implement the member function in source file
But here is my question : I try to define the whole class in header (including all the implementation of member function) ,then I found that there was no error from linker when I include this header in two source files.
Here is my header.h file
class ctr
{
public:
ctr();
ctr(char *s);
int showname(){return 0;}
private:
char *name;
};
In other two file, I include header.h
//file1.cpp
#include header.h
//file2.cpp
#include header.h
Then compile them g++ file1.cpp file2.cpp
So can anyone tell me why normal function definition will give me an error but class definition is ok?

Member functions defined within the class body are implicitly inline [class.mfct]/p2:
A member function may be defined (8.4) in its class definition, in which case it is an inline member function [..]
The inline specifier allows a function to be defined across multiple translation units.

Related

Inline functions in multiple translation units

Let's start with the following set of C++ files:
// my_class.h
struct MyClass
{
void my_func();
};
void MyClass::my_func()
{}
// f.h
void f1();
void f2();
// f1.cpp
#include "my_class.h"
void f1()
{
MyClass a;
a.my_func();
}
// f2.cpp
#include "my_class.h"
void f2()
{
MyClass a;
a.my_func();
}
// main.cpp
#include "f.h"
int main()
{
f1();
f2();
return 0;
}
I tried to compile this code with
$ g++ f1.cpp f2.cpp main.cpp
Obviously, the linker complained of duplicate symbol my_func:
duplicate symbol __ZN7MyClass7my_funcEv in:
/var/folders/yj/zz96q16j6vd1dq1_r3mz8hzh0000gn/T/f1-962ae7.o
/var/folders/yj/zz96q16j6vd1dq1_r3mz8hzh0000gn/T/f2-aef78c.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
The next attempt I had was to move the function definition inside the class definition, so we get:
struct MyClass
{
void my_func()
{}
};
Running the same g++ command, the program compiles successfully. This is because functions defined in class definition are implicitly marked inline (§10.1.6 * 3).
The standard states:
A function declaration (11.3.5, 12.2.1, 14.3) with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions specified in this section shall still be respected
Which seems somewhat in contradiction to what is written on cppreference.com:
Because the meaning of the keyword inline for functions came to mean "multiple definitions are permitted" rather than "inlining is preferred", [...]
So as far as I understand, having a function defined in the class definition makes it implicitly inline which does not necessarily mean that the compiler will choose to inline its body but will a definition of it in every translation unit. Is this correct?
The second question comes regarding template classes. Separating the declaration/definition of a template class is a problem, as described here so we can only have function definitions in the class definition which makes it implicitly inline, again, right? What is the impact of this?
Since we only have the choice of defining functions in class definitions when the class is a template, what is to be done about classes that are not template? Should we define function in source files and only keep the declaration in headers when possible?
So as far as I understand, having a function defined in the class definition makes it implicitly inline which does not necessarily mean that the compiler will choose to inline its body but will a definition of it in every translation unit. Is this correct?
Correct. When defined inside the class it is marked as inline and it is okay that that defenition is brought into multiple translation units. The compiler will handle that for you.
The second question comes regarding template classes. Separating the declaration/definition of a template class is a problem, as described here so we can only have function definitions in the class definition which makes it implicitly inline, again, right? What is the impact of this?
This is not correct. Templates are special. They aren't actually anything that will exist once the code is compiled. What a template is, is a recipe for stamping out a class or function. As such, they are implicitly inline as well to allow the template to be included in every translation unit that uses it so the compiler can stamp out a concrete class/function from it as needed. This means you can define class member functions outside of the class.
Since we only have the choice of defining functions in class definitions when the class is a template, what is to be done about classes that are not template? Should we define function in source files and only keep the declaration in headers when possible?
Typically you want to put your definitions in a cpp file. The benefit you get from this is you only need to recompile that one cpp file if you change the implementation of the functions. If they were in the header file then you need to recompile every cpp file that includes that header file which leads to longer build times.

multiple definitions of an inlined function in 2 or more TU

I was reading about inline functions and how they could have multiple definitions in different Translation Units but those definitions must be the same.
So what I was thinking is that if I declared in a header file a function and define it in 2 cpp files as inline the compiler will not be angry.
So I tried to do as follow :
global.h
#ifndef DRAKOS_GLOBAL_H
#define DRAKOS_GLOBAL_H
#include <iostream>
void fct();
#endif //DRAKOS_GLOBAL_H
file1.cpp
#include "global.h"
inline void fct()
{
std::cout << "file1.cpp \n";
}
main.cpp
#include "global.h"
extern void fct();
int main()
{
fct();
}
Builging ---> Error :
undefined reference to `fct()'
Ok I looked around in google and I read that
It’s imperative that the function’s definition be placed in a header
file, unless the function is used only in a single .cpp file.
Till now all clear and logic, but the question is, if I put the definition in the header file; every TU will have the same definition, I won't be able to define two versions of the inlined function say one in the file1.cpp TU and the second in the main.cpp TU.
So question 1 : why they say, that for an inline function, could have mutliple definitions in different TU but must be the same (as if I put the definition in the header it will be and couldn't do different in cpp file)
question 2 : If It is possible to do so and define multiple definitions of an inled function (due to miss understanding from my side) could somone plz show me an easy example :)
Thanks in advance
This code causes undefined behaviour (no diagnostic required) due to rule C++17 [dcl.inline]/6:
[...] If a function or variable with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required.
You have fct declared inline in file1.cpp but not in main.cpp.
Also you do not have any definition for fct in main.cpp. You could copy and paste into main.cpp (and every other .cpp in the project):
inline void fct()
{
std::cout << "file1.cpp \n";
}
But that would be bad practice because it would be easier to accidentally change one of the functions or forget about a .cpp file. Having it in a header file avoids this problem.

Forward declaration cause "one or more multiplied defined symbol found"?

I am using forward declaration, and I have already been careful not to have any definition inside header files (only declaration), and also have #pragma once directive before each header file. Yet somehow multiple definition error is still happening. So in GlobalSys.h, I use forward declaration, then i will include this file to any file that needs access to this global variable. In application.h, i initialize this global variable so I have to include EventManager.h or the compiler will complain. Where am I doing wrong?
GlobalSys.h
#pragma once
class EventManager;
namespace GlobalSys {
EventManager * eventManager;
}
Application.h
#include "GlobalSys.h"
#include "../Event/EventManager.h"
class Application {
public:
Application();
};
Application.cpp
#include "Application.h"
Application::Application() {
GlobalSys::eventManager = new EventManager();
}
and I have already been careful not to have any definition inside header files (only declaration)
No, you defined GlobalSys::eventManager in GlobalSys.h.
Definitions are declarations that fully define the entity introduced by the declaration. Every declaration is a definition, except for the following:
Any declaration with an extern storage class specifier or with a language linkage specifier (such as extern "C") without an initializer
You can change it to declaration with using of extern.
GlobalSys.h
#pragma once
class EventManager;
namespace GlobalSys {
extern EventManager * eventManager; // declaration
}
then define it in another implementation file, e.g.
GlobalSys.cpp
#include "GlobalSys.h"
namespace GlobalSys {
EventManager * eventManager; // definition
}
once (and other include guards) won't prevent multiple definitions. once prevents a header from being included twice by a single cpp file (translation unit). If multiple translation units include the same header, they will all have their own copy of everything in that header. That includes defined variables and functions. All these translation units are compiled separately, so any one run of the compiler doesn't know that another run already included the header and produced an object file that has its own copy of the same stuff.
The Linker, however, has to take these multiple translation units and put them together into a single program. And it finds all of the duplication. Rather than try to sort out what the programmer really wants, it gives up and asks the programmer to clarify.
Songyuanyao's answer provides one solution for this problem: Declare the variable with extern instead of defining it in the header, and then define the variable in a single translation unit. This allows multiple translation units to share a single variable. You can do the same with a function definition with the inline keyword.
Sometimes, but not this time, you want every translation unit to have their own variable. In that case
#pragma once
class EventManager;
namespace GlobalSys {
namespace {
EventManager * eventManager;
}
}
The anonymous namespace restricts eventManager's linkage to a single translation unit so each translation unit's eventManager do not conflict.

Should I put these functions in a standalone .h file?

I have a project which includes 2 files: main.cc and header1.h. While main.cc instantiates a class "className" and calls the member functions of it, the header file includes definition of the class.
main.cc:
#include <header1.h>
int main(){
className classobject();
classobject.function1();
...
}
}
header1.h:
class className{
public:
className();
void function1();
...
};
double className::function1(){
double function2(){
...
}
}
With this, I met an error which says:"a function definition should not be placed before '{' token.
So I decided to put the definition of function2 outside of the class, and put it in a standalone .h file. In that way, my project would include 3 files, namely: main.cc, head1.h and function2.h file:
main.cc:
#include <head1.h>
int main(){
className classobject;
void classobject.function1();
...
}
head1.h:
#include <function2.h>
class className{
double function2();
...
}
function2.h:
double function2(){
...
}
Although function2 can also be defined as the class member function so that it can be moved out of function1 while inside the class, I want to know whether the above mentioned treatment is leagal. Besides, does the creation of header files have some implicit rules established by usage (common treatment)?
Could anyone give some comments? Thanks in advance.
The first problem in your code that I see is that you define className::function1 in a header, but forgot to declare it inline. The outcome is that you can only use the header in a single compilation unit †. That's a bad design. I recommend defining it in a source file instead.
Secondly, you try to define function2 inside another function. That's not allowed and therefore you got a compilation error. You say that you decided to move it's definition outside the class, but it's definition was already outside the class definition. Moving the definition outside the definition of className::function1 is indeed a correct choice.
If function2 is reusable outside it's use in className::function1, then it's indeed a good idea and perfectly legal to declare it in a separate header. But just like className::function1, you're defining function2 also in the header again and again without declaring it inline and therefore you will get in trouble if you try to use it in multiple compilation units. I recommend defining function2 in a source file as well.
† If you include the header in multiple compilation units, then each of those compilation units will contain the definition of the function. The One Definition Rule does not allow defining a function in more than one compilation unit. Functions that are marked inline are treated differently. They may be defined in multiple compilation units as long as they have an identical definition in each.
Do something like this in your className.hpp
class className{
public:
className();
private:
void function1();
};
Then in your className.cpp
#include"className.hpp"
className::className(){ /*defintion*/ }
void className::function1(){ /*definition*/ }
Then you could use a makefile in oder to compute the className.o file and link it to the main file.
If you want to define another function (like function2()), you can then define it the className.cpp

Should a const static variable be initialized in a c++ header file?

my_test.h
#ifndef MY_TEST
#define MY_TEST
struct obj {
int x;
int y;
};
class A {
private:
const static int a=100;
const static obj b;
};
const obj A::b={1,2};
#endif
When compiling cpp using this header file, an error 'multiple definition of 'A::b' occurs.
why is this when I have been using guard macro already?
why does A::a not produce the erro? (I can't write code const static obj b={1,2} in class A)
why is this when I have been using guard macro already?
Header guards only prevent the inclusion of the header file contents more than once in the same translation unit not across multiple translation units.
why is A::a does not have the error message (I can't write code const static obj b={1,2} in class A)
In-class initialization is allowed by the compiler as a special case for static data members of const literal type. Your example one is In-class initialization.
const A::b defines the same symbol name in each translation unit where the header gets included and hence breaks the one definition rule.
You need to move the definition to one and only one source cpp file so that it gets defined only once.
Alok has already answered your question, but here are a few simple rules of thumb, in easy-to-memorize form:
Declarations go in the .h file
Definitions go in the .cpp file
Therefore, static members need to be declared in the .h file, and then defined in the .cpp file. In your case, fix the syntax for the declarations and then move them to "my_test.cpp" file.
The problem is your definition of A::b doesn't contain a type. To be a valid definition, it should be:
const obj A::b = {1, 2};
This will get rid of the compilation error, but you'll still get linker errors if you include this header in more than one source file, because A::b will be multiply defined then. You should move the definition into a .cpp file.
Regardless of whether you have a header guard or not, placing that initialisation in a header file will mean you'll get an instance of A::b in every source file that includes that header file. Hence the linker error.
So, generally speaking, it's possible, but not a good idea.