Navigation



8.9 — Class code and header files

Defining member functions outside the class definition

All of the classes that we have written so far have been simple enough that we have been able to implement the functions directly inside the class definition itself. For example, here’s our ubiquitous Date class:

class Date
{
private:
    int m_nMonth;
    int m_nDay;
    int m_nYear;

    Date() { } // private default constructor

public:
    Date(int nMonth, int nDay, int nYear)
    {
        SetDate(nMonth, nDay, nYear);
    }

    void SetDate(int nMonth, int nDay, int nYear)
    {
        m_nMonth = nMonth;
        m_nDay = nDay;
        m_nYear = nYear;
    }

    int GetMonth() { return m_nMonth; }
    int GetDay()  { return m_nDay; }
    int GetYear() { return m_nYear; }
};

However, as classes get longer and more complicated, mixing the definition and the implementation details makes the class harder to manage and work with. Typically, when looking at a class definition (for an already written class), you don’t care how things are implemented — you want to know how to use the class, which involves only it’s definition. In this case, all of the implementation details just get in the way.

Fortunately, C++ provides a way to separate the definition portion of the class from the implementation portion. This is done by defining the class member functions outside of the class definition. To do so, simply define the member functions of the class as if they were normal functions, but prefix the class name to the function using the scope operator (::) (same as for a namespace).

Here is our Date class with the Date constructor and SetDate() function defined outside of the class definition. Note that the prototypes for these functions still exist inside the class definition, but the actual implementation has been moved outside:

class Date
{
private:
    int m_nMonth;
    int m_nDay;
    int m_nYear;

    Date() { } // private default constructor

public:
    Date(int nMonth, int nDay, int nYear);

    void SetDate(int nMonth, int nDay, int nYear);

    int GetMonth() { return m_nMonth; }
    int GetDay()  { return m_nDay; }
    int GetYear() { return m_nYear; }
};

// Date constructor
Date::Date(int nMonth, int nDay, int nYear)
{
    SetDate(nMonth, nDay, nYear);
}

// Date member function
void Date::SetDate(int nMonth, int nDay, int nYear)
{
    m_nMonth = nMonth;
    m_nDay = nDay;
    m_nYear = nYear;
}

This is pretty straightforward. Because access functions are often only one line, they are typically left in the class definition, even though they could be moved outside.

Here is another example:

class Calc
{
private:
    int m_nValue;

public:
    Calc() { m_nValue = 0; }

    void Add(int nValue) { m_nValue += nValue; }
    void Sub(int nValue) { m_nValue -= nValue; }
    void Mult(int nValue) { m_nValue *= nValue; }

    int GetValue() { return m_nValue; }
};

becomes:

class Calc
{
private:
    int m_nValue;

public:
    Calc() { m_nValue = 0; }

    void Add(int nValue);
    void Sub(int nValue);
    void Mult(int nValue);

    int GetValue() { return m_nValue; }
};

void Calc::Add(int nValue)
{
    m_nValue += nValue;
}

void Calc::Sub(int nValue)
{
    m_nValue -= nValue;
}

void Calc::Mult(int nValue)
{
    m_nValue *= nValue;
}

In this case, we left the default constructor in the class definition because it was so short.

Putting class definitions in a header file

In the lesson on header files, you learned that you can put functions inside header files in order to reuse them in multiple files or even multiple projects. Classes are no different. Class definitions can be put in header files in order to facilitate reuse in multiple files or multiple projects. Traditionally, the class definition is put in a header file of the same name as the class, and the member functions defined outside of the class are put in a .cpp file of the same name as the class. You may sometimes hear the term “one file, one class”, which alludes to the principle of putting classes in their own separate header/code files.

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_nMonth;
    int m_nDay;
    int m_nYear;

    Date() { } // private default constructor

public:
    Date(int nMonth, int nDay, int nYear);

    void SetDate(int nMonth, int nDay, int nYear);

    int GetMonth() { return m_nMonth; }
    int GetDay()  { return m_nDay; }
    int GetYear() { return m_nYear; }
};

#endif

Date.cpp:

#include "Date.h"

// Date constructor
Date::Date(int nMonth, int nDay, int nYear)
{
    SetDate(nMonth, nDay, nYear);
}

// Date member function
void Date::SetDate(int nMonth, int nDay, int nYear)
{
    m_nMonth = nMonth;
    m_nDay = nDay;
    m_nYear = nYear;
}

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 the linker knows how Date is implemented. Don’t forget the header guards on the .h file!

In future lessons, most of our classes will be defined in the .cpp file, with all the functions implemented directly in the class definition. This is just for convenience and to keep the examples short. In real projects, it is much more common for classes to be put in their own code and header files.

8.10 — Const class objects and member functions
Index
8.8 — Constructors (Part II)

31 comments to 8.9 — Class code and header files

  • Tom

    “Fortunately, C+ provides a way…”

    ==> “Fortunately, C++ provides a way…”

    :)

    [ Fixed! Thanks! -Alex ]

  • [...] 2007 Prev/Next Posts « 8.7 — The hidden “this” pointer | Home | 8.9 — Class code and header files » Friday, September 7th, 2007 at 9:22 [...]

  • Sam

    Hi,
    I’m having some trouble with what seems like using class in the class I’m writing.

    This is the header file:

    #ifndef GUEST_H
    #define GUEST_H

    using namespace std;

    class Guest {

    private:

    string Name, Addr1, Addr2, Phone;
    int Smoking, Pets; //0=none

    public:

    // default constructor
    Guest() ;

    // destructor
    ~Guest() ;

    //…other member functions…

    };

    #endif

    The problem is, the compiler doesn’t seem to recognize when a member function tries to use Name. I’ve tried inserting std::string instead of string for the initialization, but that didn’t seem to work.

    Thanks

  • Deepu

    I think thr is another advantage of seperating implementation from defention in header files and cpp files.

    Suppose you are writing a Application Programming Interface ( a DLL or .so file) which your clients would be using, you normally give them the header files and the dynamic link libraries. As long as the signature of the methods defined in the interface does not change your client can make use of your diffrent vesions of your API. Also this is a good idea as you dont need to expose your source code to your client exposing all methods available in the API and a users manual on how to use them.

  • Ivan

    I try to go to the next page in Google Crome browser and I get this message:

    Warning: Visiting this site may harm your computer!
    The website at http://www.learncpp.com contains elements from the site 61.132.75.71, which appears to host malware – software that can hurt your computer or otherwise operate without your consent. Just visiting a site that contains malware can infect your computer.
    For detailed information about the problems with these elements, visit the Google Safe Browsing diagnostic page for 61.132.75.71.
    Learn more about how to protect yourself from harmful software online.
    I understand that visiting this site may harm my computer.

    Its crome’s defense thing. It must be coming from an ad. I know this is a good website.

    • Yeah, someone used an SQL injection attack to embed an iframe to the IP you list above into the page. I removed it so you should no longer have any trouble. Thanks for letting me know.

  • [...] 2007 Prev/Next Posts « 8.9 — Class code and header files | Home | 8.11 — Static member variables » Tuesday, September 11th, 2007 at 6:19 [...]

  • stephen

    what is the point of constructors? they are a waste of time to me, i just allocate the variables int the class

  • cpp_beginner

    Hi!
    It’s a good idea to put classes and other functions in header files but can we put them in a .dll file?If so,can you make a short tutorial of how to make a Dinamic Link Library in VC++ 08??

  • Phil

    I just tried writing my own Date class using separate files, but using default parameters for the constructor
    . I wasn’t sure if the default parameters should be in the date.h or date.cpp file (or both). With a little t
    rial and error, discovered that the default parameters should go in the header file only. Maybe this should b
    e mentioned in the tutorial?

    By the way – Fantastic tutorial :-)

  • Flying fish

    Thanks for this tutorial but I am having some trouble

    I created the date.h like this
    #ifndef DATE_H
    #define DATE_H

    class Date
    {
    private:
    int m_nMonth;
    int m_nDay;
    int m_nYear;

    Date() { } // private default constructor

    public:
    Date(int nMonth, int nDay, int nYear);

    void SetDate(int nMonth, int nDay, int nYear);

    int GetMonth();
    int GetDay() { return m_nDay; }
    int GetYear() { return m_nYear; }
    };

    #endif

    And I created the file date.cpp like this
    #include “date.h”

    // Date constructor
    Date::Date(int nMonth, int nDay, int nYear)
    {
    m_nMonth=nMonth;
    m_nDay=nDay;
    m_nYear=nYear;
    }

    // Date member function
    void Date::SetDate(int nMonth, int nDay, int nYear)
    {
    m_nMonth = nMonth;
    m_nDay = nDay;
    m_nYear = nYear;
    }
    int Date::GetMonth()
    {
    return m_nMonth;
    }

    and this is what I created for main file
    #include
    #include “date.h”
    using namespace std;
    int main()
    {
    int s, d, f;
    cout << “enter the day” <> s;
    cout << “enter the month” <>d;
    cout << “enter the year” <> f;
    Date q(s, d, f);
    return q.GetMonth();
    }

    When I run this on NetBeans on Fedora 11 I get this output

    gmake: Warning: File `nbproject/Makefile-variables.mk’ has modification time 1.2e+03 s in the future
    /usr/bin/gmake -f nbproject/Makefile-Debug.mk SUBPROJECTS= .build-conf
    gmake[1]: Entering directory `/media/disk/c++/netbeans/sample/String’
    gmake[1]: Warning: File `nbproject/Makefile-variables.mk’ has modification time 1.2e+03 s in the future
    /usr/bin/gmake -f nbproject/Makefile-Debug.mk dist/Debug/GNU-Linux-x86/string
    gmake[2]: Entering directory `/media/disk/c++/netbeans/sample/String’
    gmake[2]: Warning: File `nbproject/Makefile-variables.mk’ has modification time 1.2e+03 s in the future
    mkdir -p build/Debug/GNU-Linux-x86
    rm -f build/Debug/GNU-Linux-x86/main.o.d
    g++ -c -g -MMD -MP -MF build/Debug/GNU-Linux-x86/main.o.d -o build/Debug/GNU-Linux-x86/main.o main.cpp
    mkdir -p dist/Debug/GNU-Linux-x86
    g++ -o dist/Debug/GNU-Linux-x86/string build/Debug/GNU-Linux-x86/main.o
    build/Debug/GNU-Linux-x86/main.o: In function `main’:
    /media/disk/c++/netbeans/sample/String/main.cpp:13: undefined reference to `Date::Date(int, int, int)’
    /media/disk/c++/netbeans/sample/String/main.cpp:14: undefined reference to `Date::GetMonth()’
    collect2: ld returned 1 exit status
    gmake[2]: *** [dist/Debug/GNU-Linux-x86/string] Error 1
    gmake[2]: Leaving directory `/media/disk/c++/netbeans/sample/String’
    gmake[1]: *** [.build-conf] Error 2
    gmake[1]: Leaving directory `/media/disk/c++/netbeans/sample/String’
    gmake: *** [.build-impl] Error 2
    BUILD FAILED (exit value 2, total time: 726ms)

    Can anyone help me?

    Thanks

  • Fraz

    @Alex
    im also in a fix whether the member functions should be defined in .h or .cpp, for reusability and less redundancy it should be defined in .h so other .cpp files use it as is?

  • hellfyers

    Flying fish: you are using cout instead of cin…?

  • hakaye azure

    Hi…

    Thanks for the tutorial pages, it is magnificent.

    I have a problem over here:

    mymath.h

    #ifndef MYMATH_H
    #define MYMATH_H
    
    class mymath
    {
    	public:
    		mymath();
    		~mymath();
    		int mult(int a, int b);
    
    	private:
    };
    #endif
    

    mymath.cpp

    #include "mymath.h"
    
    mymath::~mymath() {
    
    }
    
    mymath::mymath() {
    
    }
    
    int mymath::mult(int a, int b) {
    	int c;
    	c = a * b;
    	return c;
    }
    

    mymathtest.cpp

    #include <iostream.h>
    #include "mymath.h"
    void main() {
    	mymath q;
    
    	int n;
    	n = q.mult(5, 7);
    	cout << "5 x 7 = " << n << endl;
    }
    

    compile result an error messages:
    Unresolved external ‘mymath::~mymath()’ referenced from [folder]mymathtest.obj
    Unresolved external ‘mymath::mymath()’ referenced from [folder]mymathtest.obj
    Unresolved external ‘mymath::mult(int, int)’ referenced from [folder]mymathtest.obj

    I work around this problem for hours (not the same code but the same idea), I don’t know where the codes went wrong
    and the last trial and error solve the problem, by adding a single line at the bottom of mymath.h, but is it the right way?

    #ifndef MYMATH_H
    #define MYMATH_H
    
    class mymath
    {
    	public:
    		mymath();
    		~mymath();
    		int mult(int a, int b);
    
    	private:
    };
    #include "mymath.cpp" // Here the new line added
    #endif
    
    • Gammerz

      You don’t need to add that last line of code in mymath.h

      Just add mymath.cpp to your project (I believe it gets linked in). I managed to get your original code working ok like this. Alex states:-

      Note that Date.cpp also needs to be compiled into any project that uses Date.h so the linker knows how Date is implemented.

  • all what you have written is right as long as the program is running the way you expect

  • Gio

    why don’t we just put all the code in the header file?

  • [...] 8.9 Class code and header files [...]

  • In the second-to-last paragraph you said :
    “simply #include “Date.h”. Note that Date.cpp also needs to be compiled into any project that uses Date.h so the linker knows how Date is implemented. ”

    but why we didn’t include the Date.cpp file and the compiler knows where it is ,and compiles it? this confuses me for long time ,(i am a new C++ learner) Thanks Alex!

    By the way ,this tutorial is excellent,master piece! I want to work with you! Hah, but now I am a Chinese live in China. I longing US badly!

  • Marvin

    Hello,
    I have created a child class and parent class in header files, these are the code:
    Child Class:

    #ifndef PERRO_H_
    #define PERRO_H_
    #include “Mamifero.h”

    class Perro : public Mamifero {
    public:
    Perro():raza(COLLEN){}
    ~Perro();

    RAZA getRaza() const {return raza;};
    void setRaza(RAZA _raza) {raza = _raza;};

    void moverCola() const {cout<<"Cola meneandola…"<<endl;}
    void pedirComida() const {cout<<"Pidiendo Comida…"<<endl;}

    protected:
    RAZA raza;

    };
    #endif /* PERRO_H_ */

    And the parent class:

    #ifndef MAMIFERO_H_
    using namespace std;

    enum RAZA {COLLEN, CALEN , DANDIE, SERTLAN, DOBERMAN, LAB};

    class Mamifero {
    public:
    Mamifero(): age(2), weight(5) {}
    ~Mamifero(){};
    int getAge() const {return age;}
    int getWeight() const {return weight;}
    void setWeight(int _weight) {weight = _weight;}
    void setAge(int _age) {age = _age;}

    void hablar() const {cout<<"Sonido Mamimero"<<endl;};
    void dormir() const {cout<<"shhh… esta durmiendo"<<endl;};

    protected:
    int age;
    int weight;
    };
    #endif /* MAMIFERO_H_ */

    and this i what i want to get from the main:

    #include
    #include “Mamifero.h”
    #include “Perro.h”

    using namespace std;

    int main() {
    Perro model;
    model.hablar();
    model.moverCola();
    cout<<"Model tiene "<<model.getAge() <<" anhos de edad"<<endl;

    return 0;
    }

    But i do not know what is happening with the enum data type because i recieve
    this logs in the console:

    ..\src\/Mamifero.h:11:6: error: multiple definition of 'enum RAZA'
    ..\src\/Mamifero.h:11:6: error: previous definition here
    ..\src\/Mamifero.h:13:7: error: redefinition of 'class Mamifero'
    ..\src\/Mamifero.h:13:7: error: previous definition of 'class Mamifero'

    I can not get the reason why the enum is duplicated or something similar.

    Could you please help with this.
    Thanks

  • hasith

    +++++main.cpp file++++

    #include
    #include “Hasith.h”
    using namespace std;

    int main(){
    Hasith has;
    return 0;
    }
    ______________________

    +++++Hasith.h file++++

    #ifndef HASITH_H
    #define HASITH_H

    class Hasith{
    public:
    Hasith();

    };

    #endif
    ______________________
    ++++Hasith.cpp file++++

    #include “Hasith.h”
    #include

    using namespace std;

    Hasith::Hasith(){
    cout<<"i am programming now…!"<bcc32 main.cpp
    Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
    main.cpp:
    Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland
    Error: Unresolved external ‘Hasith::Hasith()’ referenced from C:\USERS\ADMINISTR
    ATOR\DESKTOP\NEW FOLDER\MAIN.OBJ

    and, when i am compiling Hasith.cpp
    i got like this..

    C:\Users\Administrator\Desktop\New folder>bcc32 Hasith.cpp
    Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
    Hasith.cpp:
    Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland
    Error: Unresolved external ‘_main’ referenced from C:\BORLAND\BCC55\LIB\C0X32.OB
    J

    place anyone tell me what is the problem here..
    and what is the correct way to doing this…
    and according to the Hasith.h file what are the
    #ifndef HASITH_H
    #define HASITH_H
    #endif

    Thank you..

  • lloricode

    hmm what about if i want yung a string type? it is the same as int? because i use codeblock IDE hmmm its not working a seperate file if i use string type

  • gcfora

    Dear Alex,

    Many thanks from my side too for your work here, it is excellent!

    I have the following question:

    Does run-time performance get worse once we
    place the classes we use into separate .cpp (and .h header) files?

    I used your Date class above in two ways:
    1. included in the file where the main routine is and
    2. separated in a new .cpp file along with the appropriate header.

    In case 1. , the running time is so much faster than in the case 2. that
    I must be doing something wrong!

    I post the codes below:

    CASE 1.

    AllTogether.cpp
    ——————————————————————————
    #include
    #include

    using namespace std;

    class Date
    {
    private:
    int m_nMonth;
    int m_nDay;
    int m_nYear;

    Date() { } // private default constructor

    public:
    Date(int nMonth, int nDay, int nYear);

    void SetDate(int nMonth, int nDay, int nYear);

    int GetMonth() { return m_nMonth; }
    int GetDay() { return m_nDay; }
    int GetYear() { return m_nYear; }
    };

    // Date constructor
    Date::Date(int nMonth, int nDay, int nYear)
    {
    SetDate(nMonth, nDay, nYear);
    };

    // Date member function
    void Date::SetDate(int nMonth, int nDay, int nYear)
    {
    m_nMonth = nMonth;
    m_nDay = nDay;
    m_nYear = nYear;
    };

    int main ()
    {
    struct timeval tvalBefore, tvalAfter;
    gettimeofday (&tvalBefore, NULL);

    Date day(0,0,0);

    for (int i=1; i<=1000000000; i++)
    {
    Date sun(1, 1, i);
    day = sun;
    }

    cout<<day.GetDay()<<"/";
    cout<<day.GetMonth()<<"/";
    cout<<day.GetYear()<<"\n";

    gettimeofday (&tvalAfter, NULL);
    double runtime = (((tvalAfter.tv_sec – tvalBefore.tv_sec)*1000000L
    +tvalAfter.tv_usec) – tvalBefore.tv_usec)/1000000.;
    cout << "TIME "<<runtime<<" sec"<<endl;

    return 0;
    }
    ——————————————————————————

    compiled with:
    g++ -Wall -O3 AllTogether.cpp
    gives as output:

    1/1/1000000000
    TIME 6.3e-05 sec

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    CASE 2.

    Date.h:
    ——————————————————————————
    #ifndef DATE_H
    #define DATE_H

    class Date
    {
    private:
    int m_nMonth;
    int m_nDay;
    int m_nYear;

    Date() { } // private default constructor

    public:
    Date(int nMonth, int nDay, int nYear);

    void SetDate(int nMonth, int nDay, int nYear);

    int GetMonth() { return m_nMonth; }
    int GetDay() { return m_nDay; }
    int GetYear() { return m_nYear; }
    };

    #endif
    ——————————————————————————

    Date.cpp:
    ——————————————————————————
    #include "Date.h"

    using namespace std;

    // Date constructor
    Date::Date(int nMonth, int nDay, int nYear)
    {
    SetDate(nMonth, nDay, nYear);
    }

    // Date member function
    void Date::SetDate(int nMonth, int nDay, int nYear)
    {
    m_nMonth = nMonth;
    m_nDay = nDay;
    m_nYear = nYear;
    }
    ——————————————————————————

    main.cpp
    ——————————————————————————
    #include
    #include
    #include “Date.h”

    using namespace std;

    int main ()
    {
    struct timeval tvalBefore, tvalAfter;
    gettimeofday (&tvalBefore, NULL);

    Date day(0,0,0);

    for (int i=1; i<=1000000000; i++)
    {
    Date sun(1, 1, i);
    day = sun;
    }

    cout<<day.GetDay()<<"/";
    cout<<day.GetMonth()<<"/";
    cout<<day.GetYear()<<"\n";

    gettimeofday (&tvalAfter, NULL);
    double runtime = (((tvalAfter.tv_sec – tvalBefore.tv_sec)*1000000L
    +tvalAfter.tv_usec) – tvalBefore.tv_usec)/1000000.;
    cout << "TIME "<<runtime<<" sec"<<endl;

    return 0;
    }
    ——————————————————————————

    now compiled with:
    g++ -Wall -O3 Date.cpp main.cpp
    gives as output:

    1/1/1000000000
    TIME 6.05451 sec

    What is really going on here?

    Many thanks,
    gcfora

  • gcfora

    The empty
    #include
    #include

    in the codes above are meant to include

    iostream and
    sys/time.h

    Sorry about that!

    gcfora

You must be logged in to post a comment.