没有.cpp文件的C++类?

4
我不想为每个简单的C++类编写一个.cpp文件。
当我在单个.hpp文件中编写类定义和声明时,连接器会抱怨成员函数的多重定义,这些函数没有在类体内实现。
因此,我使用模板来消除连接器的投诉:
// log.hpp file
template<typename T>
class log_t {
private:
    int m_cnt = 0;
public:
    void log();
};

template<typename T>
void log_t<T>::log() {
    std::cout << ++m_cnt << std::endl;
}

// some random type (int)
typedef log_t<int> log;

然后我可以在多个.cpp文件中简单地使用log类,而不会出现链接器的问题。

这种方法有什么根本性的问题吗?

编辑:即使我使用这种方法,成员函数也会变成内联吗?


10
在需要模板时使用模板,而不仅仅是为了修复链接器错误。 - Jonathan Wakely
3
你正在使用一把锤子来拧螺丝。 - Salgar
1
我不想为每一个简单的C++类都写一个.cpp文件。相反,为每个.h (或.hpp)文件编写一个.cpp文件。如果这个类足够大,可以有自己的头文件,那就给它一个自己的实现。如果它足够小,可以与其他类一起分组,对它们的实现也同样如此。 - mah
1
添加一个头文件保护可能有助于类似的情况。 - jvriesem
3个回答

6
如果某个类不需要成为模板,不要仅仅因为“想摆脱链接器的抱怨”而将其变成模板。可以采用适当的编码技巧,例如提供成员函数的内联定义:
// log.hpp file

#pragma once

#include <iostream> // For std::cout, std::endl

class log {
private:
    static int m_cnt = 0;
public:
    log();
};

// Note "inline" here:
inline log::log() {
    std::cout << ++m_cnt << std::endl;
}

如果您在类定义内部提供函数体,则无需指定关键字inline:
// log.hpp file

#pragma once

#include <iostream> // For std::cout, std::endl

class log {
private:
    static int m_cnt = 0;
public:
    // Function body inserted here (no need for "inline"):
    log() {
        std::cout << ++m_cnt << std::endl;        
    }
};

3

由于你希望在头文件中定义函数(这些头文件被多个源文件包含,导致在你的情况下出现多次定义),因此应该将这些函数设置为inline

有两种选择:

  • 隐式inline-不要分离函数定义
  • 显式inline-在定义前手动添加inline关键字

仅为了“解决”此错误而使用template并不是一个好主意。只有在需要时才使用模板。


这些差异会带来什么影响? - amin
无论如何,这些函数都将是内联的。 - Jonathan Wakely
@user1575632 - 两个选项都是一样的。选择你喜欢的那一个。 - Kiril Kirov

1

这个方法有什么问题吗?这个方法本质上有什么问题吗?

没有问题。你需要遵守 One Definition Rule,做以下事情:

  1. 通过将函数定义放在声明内或在每个函数声明前加入inline关键字来使函数定义成为内联的,以避免多个翻译单元(cpp文件)中的多个定义。
  2. 在头文件中放置包含保护程序。这是为了避免在同一个翻译单元中多次包含。

实际上,这是用于template工作的唯一方法。你不能将模板定义放在cpp文件中并期望它能够工作,因为C ++编译器需要定义来实例化模板。使用template不是解决此方法工作的方法,而是要求你只使用此方法才能使你的代码编译。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接