C++ 如何在源文件中定义类并在头文件中声明它(而不必使用`class::method`语法定义类方法)?

C++ 如何在源文件中定义类并在头文件中声明它(而不必使用`class::method`语法定义类方法)?,c++,class,header-files,declaration,one-definition-rule,C++,Class,Header Files,Declaration,One Definition Rule,我正在查看一个在其中一个头文件中包含以下内容的文件: 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; re

我正在查看一个在其中一个头文件中包含以下内容的文件:

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(); }
};
为什么当我用下面的替换源文件中的上述内容时,编译器会抱怨重新定义?我认为头文件只包含声明,而源文件实际上定义了类

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 ) );
} 
}

如何使用上述样式而不是Util::log来定义类?

当您有一块类似

class Util
{
    ...
}
您正在定义类。您给出的第一个示例是定义类并在头文件中声明函数。源文件正在定义函数

当您试图将类Util行和大括号放在源文件中时,编译器认为您正在定义一个新类。这就是为什么你会出错

如果你只是把类Util;在一行中,您将声明该类。类声明告诉您该类存在,但不告诉您有关该类的任何信息

如果删除了头文件并将以下内容放在源文件中,它将被编译,但其他源文件将无法使用该类,因为它们将不再具有为它们定义该类的头文件

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 ) );
    } 
}
如果希望一个类由多个源文件使用,则需要在头文件中定义该类。然后,您可以在头文件中定义函数,因为很多原因通常不鼓励这样做,或者您可以使用Util::log语法在源文件中定义函数,这是推荐的做法


头文件及其使用方式是C和C++的常见抱怨。这就是为什么像Java和C这样的较新语言倾向于不使用头文件的原因。但是,您引用的库定义了类,应该根据C++最佳实践定义类。

当您有一个代码块时,比如

class Util
{
    ...
}
您正在定义类。您给出的第一个示例是定义类并在头文件中声明函数。源文件正在定义函数

当您试图将类Util行和大括号放在源文件中时,编译器认为您正在定义一个新类。这就是为什么你会出错

如果你只是把类Util;在一行中,您将声明该类。类声明告诉您该类存在,但不告诉您有关该类的任何信息

如果删除了头文件并将以下内容放在源文件中,它将被编译,但其他源文件将无法使用该类,因为它们将不再具有为它们定义该类的头文件

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 ) );
    } 
}
如果希望一个类由多个源文件使用,则需要在头文件中定义该类。然后,您可以在头文件中定义函数,因为很多原因通常不鼓励这样做,或者您可以使用Util::log语法在源文件中定义函数,这是推荐的做法


头文件及其使用方式是C和C++的常见抱怨。这就是为什么像Java和C这样的较新语言倾向于不使用头文件的原因。但是,您引用的库定义了类,应该根据C++最佳实践来定义类。

您真正的问题是误解了类定义。您所描述的类定义不是类定义

github头中的类声明实际上是类Util的定义。在该定义中,成员函数toString实际上还定义了Util::toString

然后,源文件定义了该类的两个成员函数,可能是在包含了头之后,尽管您还没有显示这一点。这两个函数定义不是类的定义

通常的做法实际上是github库所做的,您正在尝试改变

在头文件中定义类。这样,需要使用该类的每个编译单元都包括头,并可以看到类定义。这对于非平凡地使用类是必要的,例如实例化实例、访问成员等。 在编译单元中定义成员函数,在编译单元中成员函数只能定义一次,或者在头中定义为内联函数,在内联函数中,多个编译单元可以看到函数定义。在使用这些函数时,编译器负责防止出现多定义问题。
当您更改它时,您已经在源文件中引入了类Util的定义。如果在包含头之后发生这种情况,编译器将看到类Util的两个定义。这就是编译器抱怨定义的原因。

您真正的问题在于您误解了类定义是什么。您所描述的类定义不是 类定义

github头中的类声明实际上是类Util的定义。在该定义中,成员函数toString实际上还定义了Util::toString

然后,源文件定义了该类的两个成员函数,可能是在包含了头之后,尽管您还没有显示这一点。这两个函数定义不是类的定义

通常的做法实际上是github库所做的,您正在尝试改变

在头文件中定义类。这样,需要使用该类的每个编译单元都包括头,并可以看到类定义。这对于非平凡地使用类是必要的,例如实例化实例、访问成员等。 在编译单元中定义成员函数,在编译单元中成员函数只能定义一次,或者在头中定义为内联函数,在内联函数中,多个编译单元可以看到函数定义。在使用这些函数时,编译器负责防止出现多定义问题。 当您更改它时,您已经在源文件中引入了类Util的定义。如果在包含头之后发生这种情况,编译器将看到类Util的两个定义。这就是编译器抱怨定义的原因