Search code examples
c++classheader-filesdeclarationone-definition-rule

How to define a class in a source file and declare it in a header file (without having to define the class methods using `class::method` syntax)?


I am looking at a library on github which has the following in one of the header files:

class Util
{
public:
    static void   log( const string& message );
    static void   log( const char* message );
    template<typename T>
    static inline string toString(T t) { stringstream s; s << t; return s.str(); }
};

and the following in the source file:

void Util::log( const string& message )
{
    const string& logMessage = "[cppWebSockets] " + message;
    syslog( LOG_WARNING, "%s", logMessage.c_str( ) );
}

void Util::log( const char* message )
{
    log( string( message ) );
}

Why is it that when I replace the above contents in the source file with the below the compiler complains of "redefinition"? I thought the header file only held the declaration whereas the source file actually defines the class?

class Util
{
public:
    void Util::log( const string& message )
{
    const string& logMessage = "[cppWebSockets] " + message;
    syslog( LOG_WARNING, "%s", logMessage.c_str( ) );
}

void Util::log( const char* message )
{
    log( string( message ) );
} 
}

How to define the class using the above style instead of Util::log?


Solution

  • When you have a block of code like

    class Util
    {
        ...
    }
    

    you are defining the class. The first sample you gave is defining the class and declaring the functions in the header file. The source file is defining the functions.

    When you tried to put the class Util line in the source file with the braces, the compiler thinks you are defining a new class. That's why you get the error.

    If you just put class Util; on a line, then you would be declaring the class. A class declaration tells you that the class exists, but does not tell you anything about the class.

    If you removed the header file and put the following in the source file, it would compile, but other source files would not be able to use the class (because they would no longer have the header file defining it for them).

    class Util
    {
    public:
        void log( const string& message )
        {
            const string& logMessage = "[cppWebSockets] " + message;
            syslog( LOG_WARNING, "%s", logMessage.c_str( ) );
        }
    
        void log( const char* message )
        {
            log( string( message ) );
        } 
    }
    

    If you want to have a class be used by multiple source files, then you will need to define the class in a header. You could then define the functions inside the header as well (usually discouraged for a lot of reasons), or you could use the Util::log syntax to define the function in a source file (the recommended practice).

    Header files (and the way they need to be used) are a common complaint about C and C++. That is why newer languages (like Java and C#) tend not to use header files. However, the library you referenced is defining this class the way classes should be defined according to C++ best practices.