是否可以在同一个.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,以便我们可以审查这种可能性:< / p>
#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,@ immilind和@Jerry Coffin,我们有了解决方案。从AbstractConnection中删除析构函数后(因为它实际上什么都不做)并从ConnectionMockup中删除Q_OBJECT它就可以了。
答案 0 :(得分:17)
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;
}
注意类定义下的`#include“myfile.moc”。
这是有效的,因为qmake将使用#include指令在任何文件上调用MOC工具。因此,MOC将解析.cpp文件并生成元对象函数定义,解决链接器错误。
答案 1 :(得分:14)
是的,在一个文件中定义一个类及其成员函数是完全合法的,也是允许的。实际上,从编译器的角度来看,实际上总是如此 - 在头文件中有类定义,并在源文件中包含实现其成员函数的头文件。
您遇到的错误看起来像链接器错误,而不是编译器错误。究竟缺少什么并不完全清楚你发布的内容。一种可能性是你的基类有一些你未能在派生类中实现的纯虚拟,但我根本不是 确定这是正确的。
答案 2 :(得分:3)
当Base类具有非纯的任何virtual
函数时,在编译最终二进制文件时需要包含它的定义,否则它会为vtable
或者typeinfo
提供链接器错误// 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;
}
。请看下面的例子:
class AbstractConnection
现在Derived.cpp和Base.cpp的compile-link工作正常。两个.cpp文件也可以单独编译以创建目标文件,然后链接在一起。
从你的问题来看,我的感觉是,你并没有以某种方式附加destructor
的.cpp / object文件,它仍包含一个非纯虚函数 - ConnectionMockup
。如果您与{{1}}一起编译该定义,则不应出现链接器错误。您可以编译包含析构函数体的文件,也可以在类定义本身中定义析构函数体。