1

Why are functions usually declared in classes and not defined?:

class MyClass {
public:
  MyFunction(); //Function is declared not defined
};

Instead of:

class MyClass {
public:
  MyFunction() {
      std::cout << "This is a function" << std::endl;
  } //Function is defined instead of being declared. No need for cpp file
};

Is the reason because it looks nice or easier to read?

4 Answers4

3

The general (theoretical) reason is separation of concerns: to call a function (whether a class member function or not) the compiler only needs visibility of the function's interface, based on its signature (name, return type, argument types). The caller does not need visibility of function definitions (and, if the definition is not (or cannot be) inlined for some reason, the outcome is often a program that fails to compile or link).

Two common practical reasons are to maximise benefits of separate compilation and to allow distribution of a library without source code.

Separate compilation (over-simplistically, placing subsets of source code in distinct source files) is a feature of C++, that has numerous benefits in larger projects. It involves having a set of separately compiled source files rather than throwing all the source for a program into a single source file. One benefit of separate compilation is that it enables incremental builds. Once all source files for a project have been compiled, the only source files that need to be recompiled are those that have changed (since recompiling a source file that hasn't changed produces a functionally equivalent object file). For large projects that are edited and rebuilt often, this allows incremental builds (recompiling changed source files only and relinking) instead of universal builds (which recompile and relink everything). Practically, even in moderately large projects (lets' say projects that consist of a few hundred source files) the incremental build time can be measured in minutes while the universal rebuild time can be measured in days. The difference between the two equates to unproductive time (thumb-twiddling waiting for a build to complete) by programmers. Programmer time is THE largest cost in significant projects, so unproductive programmer time is expensive.

In practice, in moderate or larger projects, function interfaces (function signatures) are quite stable (change relatively infrequently) while function definitions are unstable (change relatively frequently). Having a function definition in a header file means that header file is more likely to be edited as the program evolves. And, when a header file is edited, EVERY source file which includes that header file must be recompiled in order to rebuild (build management tools often work that way, because it's the least complicated way to detect dependencies between files in a project). That tends to result in larger build times - while the impacts of recompiling all source files that include a changed header are not as large as doing an universal build, the impacts can involve increasing the incremental build time from a few minutes to a few hours. Again, more unproductive time for programmers.

That's not saying that functions should never be inlined. It does mean that it is necessary to choose carefully which functions should be inlined (e.g. based on performance benefits identified using profiling, avoid inlining functions that will be updated regularly) rather than (as this question suggests) defaulting to inlining.

The second common reason for not inlining functions is for distribution of libraries. For commercial (e.g. protecting intellectual property) and other reasons it is often preferable to distribute a compiled library and header files (the minimum needed for someone to use a library in another project) without distributing the complete source code. Placing functions into a header file increases the amount of source file that is distributed.

Peter
  • 35,646
  • 4
  • 32
  • 74
0

For non-template code placing definitions in source files separate from the declaration allows for more efficient compilation. You have a single source file that needs to be compiled, so long as that file doesn't change the output can be reused. The final link step isn't effected very much by having multiple input files, the symbols will need to be resolved from the same data structures whether there is one input file or many. It's harder with C++ but this can even allow distribution of binary-only libraries, that is, distributing headers containing only declarations and matching object files, without the implementing source at all.

On the other hand, if functions are defined inline (whether in the class body or outside with explicit 'inline' keyword) the linker will have to check its symbol table and toss duplicates. And that applies regardless of whether talking about templates or not. But templates need their definitions to be available in every translation unit that uses the template so their definitions will generally go in headers even with the cost of pruning duplicates at the link stage.

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
-1

Generally, classes are declared in header files. Writing the definition in the header would run afoul of the one-definition rule. If I have

// my_class.h

class MyClass {
public:
  void myFunction() {}
};

Then, as soon as I've included this file in two different .cpp files, I have two separate definitions of MyClass.myFunction, which is an error. Thus, we declare our class methods in the header and then write the implementation in the .cpp source file.

// my_class.h

class MyClass {
public:
  void myFunction();
};

// my_class.cpp

void MyClass::myFunction() {}

The rules are more complicated if the function is inline or a template function, in which case there's a compelling (and often required) reason to define the function in the header. Some people will define template functions in the class itself as you've suggested, but others prefer to write the template function definition at the bottom of the header, to be more consistent with the way we write non-template functions. It's more a matter of style in this case, so pick your preferred convention and stick to it.

Likewise, if the class is not mentioned in a header file (i.e. if the class is an implementation detail and only exists in one .cpp file), then you're free to do it either way, and you'll find folks that prefer both conventions.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • I would add on how it affects compilation plus distribution of headers for linking about libraries. – Taekahn Dec 31 '21 at 01:13
  • 4
    non static member functions defined in the class body are implicitly inline, and so do not violate ODR. – JohnFilleau Dec 31 '21 at 01:15
  • @JohnFilleau I did not know that. Perhaps you could write an answer that goes into more detail; I could learn something on the topic today too :P – Silvio Mayolo Dec 31 '21 at 01:19
  • I wrote my comment 5 minutes ago so I can't edit it. A program can violate ODR even if it has inline functions, if the same inline function has different definitions in different translation units (perhaps two parts of your program including different versions of a header only library). So my comment is not always true. Implicitly inline does not always imply ODR-safe. – JohnFilleau Dec 31 '21 at 01:25
  • I don't think I would include these implicitly inline details in an answer to the question. It's tangential, but supports defining in the header file (as opposed to defining in the source file). So I'll leave it as a comment to your answer. – JohnFilleau Dec 31 '21 at 01:26
-1

in one word, seperate implemention and declearation is always good.

in a large project, implemention in header file will cause multiple definition problem.

seperate them is always good way, can avoid a lot of future problems.

nick
  • 832
  • 3
  • 12
  • You can put implementations in the header file and ODR will not be violated as long as either: 1. the functions don't appear in more than one translation unit, OR 2. the functions are inline and have the same definition in all translation units. – JohnFilleau Dec 31 '21 at 01:30
  • Plenty of header only libraries exist. Like the…stl. – Taekahn Dec 31 '21 at 01:38
  • @Taekahn — if by “the…stl” you mean the standard library hat comes with your compiler, it’s not a header-only library. The compiler driver knows about the standard library, so you don’t have to explicitly link to it, but there is a library file there. – Pete Becker Dec 31 '21 at 04:01
  • So how did they manage to separate all those template definitions into precompiled binaries? I would like to learn this trick. – Taekahn Dec 31 '21 at 04:02