C ++和虚方法覆盖

时间:2010-06-16 12:12:29

标签: c++ qt

抱歉这个愚蠢的问题,但我自己也找不到答案,我在C ++中太新了:(

class DBObject : public QObject
{
    ...
protected:
    virtual QString tableName() = 0;
};

class DBUserObject : public DBObject
{
    ...
protected:
    virtual QString tableName()  { return "profiles"; };
};

我在父母的代码中有这个代码:

DBObject::DBObject(quint32 id)
    : QObject(0)
{
    ...    

    if (id != 0)
        load(id);
}

bool DBObject::load(quint32 id)
{
    QString query = QString("select %1 from %2 where id = :id")
        .arg(fieldList().join(","))
        .arg(tableName());              <--- here is trouble
    ...
}

所以我正在尝试执行:

DBUserObject user(3);

但结果我有一个运行时错误。为什么不“个人资料”?

6 个答案:

答案 0 :(得分:15)

根据OP的后续评论:

  

DBUserObject用户(3)。它正在加载   其构造函数中的项目。

如果你的意思是DBObject构造函数(而不是DBUserObject构造函数),那就是你的问题。虚函数在构造函数中不起作用。构造函数从最小派生(最基础)类运行到最派生(实际类型)类。当一个类的构造函数运行时,该对象只是该类的类型,而不再是派生的。

换句话说,当您创建DBUserObject时,首先运行QObject构造函数,并且在该构造函数中,该对象仅为QObect而已。然后,DBObject构造函数运行,并且在该构造函数内,该对象仅为DBObject,仅此而已。最后,DBUserObject构造函数运行,对象最终为DBUserObject

因此,如果您在load()构造函数内部调用DBObject,那么该对象在此时仅为DBObject,因此只有DBObject版本的加载。这同样适用于任何虚拟功能。

如果您想获得调用DBUserObject load()版本的效果,则需要从DBUserObject构造函数调用它,或者在对象具有之后从类外部调用它已经建成。

更多信息:

答案 1 :(得分:3)

问题很可能不在您提供的代码中。你在切片DBObject吗?如果将值传递给函数,或者直接存储在容器内(而不是通过指针),则可能发生这种情况。

另一件事是为什么基类中tableName()不是纯虚拟的?

答案 2 :(得分:2)

你可以使用'pure virtual'函数来确保只能使用子类,因为基类(DbObject)没有表名。

这将强制DbObject的所有实例化(通过继承的类)具有有效的表名

例如: virtual QString tableName() = 0;

答案 3 :(得分:1)

首先,使用Google阅读C ++中的“对象切片”。在C ++中切片对象很容易(但错误),特别是对于新手:如果按值传递一个对象(通常是错误的)而不是通过引用传递(通常是正确的),则会发生切片,例如,如果您声明了“DBObject”类型的参数(错误)而不是“DbObject&amp;”或“const DbObject&amp;” (右)。

其次,将以下语句添加到DBObject类:

class DBObject : public QObject 
{ 
    ... 
protected: 
    virtual QString tableName()  { return ""; }; 
private:
    //non-default, unimplemented copy ctor and assignment operator
    DBObject(const DBObject&);
    DBObject& operator=(const DBObject&);
}; 

声明非默认的,未实现的复制和赋值将导致编译时错误,其中我们尝试按值传递DBObject:因此,第三步是通过更改参数类型以通过引用传递来修复这些错误。

答案 4 :(得分:0)

你不应该内联虚函数,因为有些编译器无法很好地处理它。

您应该将DBObject :: tableName()和DBUserObject :: tableName的实现移动到.cpp文件。

答案 5 :(得分:0)

这里你似乎没有做错任何事。你确定你的QString :: arg(...)方法没有问题吗?

明确地调用this-&gt; tableName();看起来像编译器问题。

- 更新 -

实际上你对tableName()的定义应该是

virtual void tableName() const { ... }

确保你的operator = for QString是有序的(const和非const版本),可能是从tableName()返回的QString是一个临时的堆栈,在这种情况下运算符=将被称为......