All of the classes that we have written so far have been simple enough that we have been able to implement the member functions directly inside the class definition itself. For example, here here's simple Date
class where all member functions are defined inside the Date
class definition:
#include <iostream>
class Date
{
private:
int m_year{};
int m_month{};
int m_day{};
public:
Date(int year, int month, int day)
: m_year { year }
, m_month { month }
, m_day { day}
{
}
void print() const { std::cout << "Date(" << m_year << ", " << m_month << ", " << m_day << ")\n"; };
int getYear() const { return m_year; }
int getMonth() const { return m_month; }
int getDay() const { return m_day; }
};
int main()
{
Date d { 2015, 10, 14 };
d.print();
return 0;
}
However, as classes get longer and more complicated, having all the member function definitions inside the class can make the class harder to manage and work with. Using an already-written class only requires understanding its public interfaces (the public member functions), not how the class works underneath the hood. The member function implementations clutter up the public interface with details that aren't relevant to actually using the class.
To help address this, C++ allows us to separate the “declaration” portion of the class from the "implementation" portion by defining member functions outside of the class definition.
Here is the same Date
class as above, with the constructor and print()
member functions defined outside the clas definition. Note that the prototypes for these member functions still exists inside the class definition (as these functions need to be declared as part of the class type definition), but the actual implementation has been moved outside:
#include <iostream>
class Date
{
private:
int m_year{};
int m_month{};
int m_day{};
public:
Date(int year, int month, int day)
: m_year { year }
, m_month { month }
, m_day { day}
{
}
void print() const { std::cout << "Date(" << m_year << ", " << m_month << ", " << m_day << ")\n"; };
int getYear() const { return m_year; }
int getMonth() const { return m_month; }
int getDay() const { return m_day; }
};
int main()
{
Date d { 2015, 10, 14 };
d.print();
return 0;
}
Member functions are defined outside the class type just like non-member functions. The only difference is that we must prefix the member function names with the name of the class type (in this case, Date::
) so the compiler knows we are defining a member of that class type rather than a non-member.
Putting class definitions in a header file
If you define a class inside a source (.cpp) file, that class is only usable withing that particular source file. In larger programs, it's common that we will want to use the classes we write in multiple source files.
We already leaned that we can put function declarations in a header files. Then we can #include
those functions declarations into multiple code files (or even multiple projects). Classes are no different. A class definitions can be put in a header files, and then #included
into any other files that want to use the class type.
Naming your class header and code files
Most often, classes are defined in header files of the same name as the class, and any member functions defined outside of the class are put in a .cpp
file of the same name as the class.
Here's our Date class again, broken into a .cpp and .h file:
Date.h:
#ifndef DATE_H
#define DATE_H
class Date
{
private:
int m_year{};
int m_month{};
int m_day{};
public:
Date(int year, int month, int day);
void SetDate(int year, int month, int day);
int getYear() const { return m_year; }
int getMonth() const { return m_month; }
int getDay() const { return m_day; }
};
#endif
Date.cpp:
#include "Date.h"
// Date constructor
Date::Date(int year, int month, int day)
{
SetDate(year, month, day);
}
// Date member function
void Date::SetDate(int year, int month, int day)
{
m_month = month;
m_day = day;
m_year = year;
}
Now any other header or code file that wants to use the Date
class can simply #include “Date.h”
. Note that Date.cpp also needs to be compiled into any project that uses Date.h so that linker can connect calls to the member functions to their definitions.
So why not put everything in a header file?
You might be tempted to put all your member function definitions into the header file, either insider the class definition, or as inline functions below the class definition. While this will compile, there are a couple of downsides to doing so.
- First, as mentioned above, defining members inside the class definition clutters up your class definition.
- Second, if you change any of the code in the header, then you will need to recompile every file that includes that header. This can have a ripple effect, where one minor change cause the entire program to need to recompile (which can be slow). If you change the code in a
.cpp
file, only that.cpp
file needs to be recompiled.