将类声明放入.cpp文件中

Putting class declaration in .cpp file

本文关键字:cpp 文件 声明      更新时间:2023-10-16

是否可以在相同的。cpp文件中进行类声明和实现?

我想在mock对象的帮助下做一些单元测试。下面是我测试的一些例子:

// 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(...
...
...

可以看到,类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'

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

编辑:由于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;
};

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

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

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

注意' #include "myfile。类定义下的Moc"。

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

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

您遇到的错误看起来像是链接器错误,而不是编译器错误。从你发布的信息中还不完全清楚到底缺了什么。一种可能性是你的基类有一些你没有在派生类中实现的纯虚函数,但是我完全不确定这是正确的。

当基类有非纯virtual函数时,在编译最终二进制文件时需要包含该函数的定义,否则会导致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;
}

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

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