C++ 将类声明放在 .cpp 文件中

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5657528/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:37:11  来源:igfitidea点击:

Putting class declaration in .cpp file

c++unit-testingqt

提问by Sasa

Is it possible to have class declaration and implementation in same .cpp file?

是否可以在同一个 .cpp 文件中进行类声明和实现?

I want to do some unit-testing with help of mock object. Here is some example of my test:

我想在模拟对象的帮助下进行一些单元测试。这是我的测试的一些示例:

// Some includes removed

#include "abstractconnection.h"

class ConnectionMockup : public AbstractConnection
{
    Q_OBJECT
public:
    explicit ConnectionMockup(QObject *parent = 0);

    bool isReady() const;
    void sendMessage(const QString &message);

    void test_send_message(const QString &message);

    bool ready;
    QStringList messages;
};

ConnectionMockup::ConnectionMockup(QObject *parent)
    : AbstractConnection(parent)
{
    ready = true;
}

bool ConnectionMockup::isReady() const
{
    return ready;
}

void ConnectionMockup::sendMessage(const QString &message)
{
    messages.append(message);
}

void ConnectionMockup::test_send_message(const QString &message)
{
    emit messageRecieved(message);
}

TestEmcProgram::TestEmcProgram(QObject *parent) :
    QObject(parent)
{
}

void TestEmcProgram::open()
{
    ConnectionMockup mockup;
    EmcProgram program(&mockup);
    QCOMPARE(...
...
...

As you can see, the class ConnectionMockup is only used by class TestConnection, and I don't need it anywhere else. So, when I try to compile this program, I get following error:

如您所见,ConnectionMockup 类仅由 TestConnection 类使用,我在其他任何地方都不需要它。因此,当我尝试编译此程序时,出现以下错误:

> testemcprogram.o: In function  
> `ConnectionMockup':  
> /home/sasa/Desktop/QtPro/FocoKernel-build-desktop/../FocoKernel/testemcprogram.cpp:29:  
> undefined reference to `vtable for  
> ConnectionMockup'  
> /home/sasa/Desktop/QtPro/FocoKernel-build-desktop/../FocoKernel/testemcprogram.cpp:29:  
> undefined reference to `vtable for  
> ConnectionMockup' testemcprogram.o: In  
> function `~ConnectionMockup':  
> /home/sasa/Desktop/QtPro/FocoKernel-build-desktop/../FocoKernel/testemcprogram.cpp:14:  
> undefined reference to `vtable for  
> ConnectionMockup'

Is it possible to leave declaration here, or I must create header file and move declaration in to that file?

是否可以在此处保留声明,或者我必须创建头文件并将声明移动到该文件中?

EDIT:Since Mr. Jerry Coffin (thank you Mr. Coffin) suggested that I may not have some virtual functions implemented, I will put here declaration of AbstractConnection so we could review that possibility:

编辑:由于 Jerry Coffin 先生(谢谢 Coffin 先生)建议我可能没有实现一些虚函数,我将在此处放置 AbstractConnection 的声明,以便我们可以这种可能性:

#include <QObject>

class AbstractConnection : public QObject
{
    Q_OBJECT
public:
    explicit AbstractConnection(QObject *parent = 0);
    virtual ~AbstractConnection();

    virtual bool isReady() const = 0;

signals:
    void messageRecieved(const QString &message);

public slots:
    virtual void sendMessage(const QString &message) = 0;

};

SOLUTION:Thanks to @JCooper, @iammilind and @Jerry Coffin we have the solution. After removing destructor from AbstractConnection (since it actually does nothing) and removing Q_OBJECT from ConnectionMockup it works.

解决方案:感谢@JCooper、@iammilind 和@Jerry Coffin,我们有了解决方案。从 AbstractConnection 中删除析构函数(因为它实际上什么都不做)并从 ConnectionMockup 中删除 Q_OBJECT 后,它就可以工作了。

采纳答案by Jerry Coffin

Yes, it's entirely legitimate and allowable to define a class and its member functions in a single file. In fact, from the viewpoint of the compiler that's essentially always the case -- you have the class definition in a header, and include that header in the source file where you implement its member functions.

是的,在单个文件中定义一个类及其成员函数是完全合法和允许的。事实上,从编译器的角度来看,本质上总是如此——您在头文件中有类定义,并将该头文件包含在实现其成员函数的源文件中。

The errors you've encountered look like linker errors, not compiler errors. Exactly what's missing isn't entirely clear from what you've posted. One possibility is that your base class has some pure virtuals that you've failed to implement in the derived class, but I'm not at allsure that's correct.

您遇到的错误看起来像是链接器错误,而不是编译器错误。从您发布的内容来看,到底缺少什么并不完全清楚。一种可能性是您的基类有一些您未能在派生类中实现的纯虚拟,但我完全不确定这是正确的。

回答by Elegant Codeworks

The Q_OBJECTmacro declares a set of meta-object member functions. The MOC build tool is responsible for parsing .h files and defining these function declarations. Note that it does not parse .cpp files. In your case, the vtablecould not be found because the MOC tool did not parse your .cpp file. The solution is to move your class definition inside a header file and add the header to your .pro file. A second solution - a bit "hacky" - is to do the following:

Q_OBJECT宏声明了一组元对象成员函数。MOC 构建工具负责解析 .h 文件并定义这些函数声明。请注意,它不会解析 .cpp 文件。在您的情况下,vtable无法找到,因为 MOC 工具没有解析您的 .cpp 文件。解决方案是将类定义移动到头文件中,并将头添加到 .pro 文件中。第二种解决方案 - 有点“hacky” - 是执行以下操作:

#include <QObject>
#include <QtDebug>

class Counter : public QObject
{
  Q_OBJECT

public:
  Counter() { value = 0; }
  int getValue() const { qDebug() << "getValue()"; return value; }

public slots:
  void setValue(int value);

signals:
  void valueChanged(int newValue);

private:
  int value;
};

#include "main.moc"

void Counter::setValue(int value)
{
  qDebug() << "setValue()";
  if (this->value != value) {
    this->value = value;
    emit valueChanged(value);
  }
}

int main()
{
  Counter a, b;

  QObject::connect(
    &a, &Counter::valueChanged,
    &b, &Counter::setValue);

  a.setValue(12);
  b.setValue(48);

  return 0;
}

Notice the `#include "myfile.moc" under the class definition.

注意类定义下的`#include "myfile.moc"。

This works because qmake will invoke the MOC tool on any files with a #include directive. Thus, MOC will parse the .cpp file and generate the meta-object function definitions, resolving your linker error.

这是有效的,因为 qmake 将使用 #include 指令对任何文件调用 MOC 工具。因此,MOC 将解析 .cpp 文件并生成元对象函数定义,从而解决您的链接器错误。

回答by iammilind

When Base class has any virtualfunction which is not pure, it's definition needs to be included while compiling the final binary, otherwise it gives linker error for vtableor typeinfo. Look at below example:

当 Base 类具有任何非 purevirtual函数时,编译最终二进制文件时需要包含它的定义,否则会为or提供链接器错误。看下面的例子:vtabletypeinfo

// Base.h
struct Base {
  virtual void fun() = 0;
  virtual ~Base();
};

// Base.cpp
#include"Base.h"
Base::~Base () {}

// Derived.cpp
#include"Base.h"
struct Derived : Base {
  void fun () {}
};

int main () {
  Derived d;
}

Now compile-link for Derived.cpp and Base.cpp will work fine. Both .cpp files also can be compiled separately for creating object files and then linked together.

现在 Derived.cpp 和 Base.cpp 的编译链接将正常工作。两个 .cpp 文件也可以单独编译以创建目标文件,然后链接在一起。

From your question, what I feel is that, you are not somehow attaching the .cpp/object file of class AbstractConnection, which still contains one non pure virtual function -- its destructor. If you compile that definition also along with your ConnectionMockupthen the linker error should not appear. Either you can compile the file including destructor body or define destructor body in the class definition itself.

从你的问题来看,我的感觉是,你没有以某种方式附加 .cpp/object 文件class AbstractConnection,它仍然包含一个非纯虚函数——它的 .cpp/object 文件destructor。如果您同时编译该定义,ConnectionMockup则不应出现链接器错误。您可以编译包含析构函数体的文件,也可以在类定义本身中定义析构函数体。