开发者

C++ and virtual methods overriding

开发者 https://www.devze.com 2023-01-04 03:38 出处:网络
Sorry for this stupid question, but I can\'t find an answer by myself, I\'m too new in C++ :( class DBObject : public QObject

Sorry for this stupid question, but I can't find an answer by myself, I'm too new in C++ :(

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

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

And I have this code in parent:

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
    ...
}

So I'm trying to execute:

DBUserObject user(3);

But in result I have a runtime error. Why not "profiles"?


Based on the OP's followup comment:

DBUserObject user(3). It is loading item in its constructor.

If you mean the DBObject constructor (and not the DBUserObject constructor), then there's your problem. Virtual functions do not work inside constructors. Constructors run from the least-derived (most base) class to the most-derived (actual type) class. When a class' constructor runs, the object is only of that class' type, and nothing more derived.

In other words, when you create a DBUserObject, first the QObject constructor runs, and inside that constructor the object is only a QObect and nothing more. Then, the DBObject constructor runs, and inside that constructor the object is only a DBObject and nothing more. Finally, the DBUserObject constructor runs and the object is finally a DBUserObject.

So if you call load() inside of the DBObject constructor, the object is only a DBObject at that point and so has only the DBObject version of load. This applies similarly for any virtual function.

If you want to get the effect of calling the DBUserObject version of load(), you will need to call it from the DBUserObject constructor, or from outside the class after the object has been constructed.

More information:

  • http://www2.research.att.com/~bs/bs_faq2.html#vcall
  • http://www.artima.com/cppsource/nevercall.html


The problem is most probably not in the code you provided. Are you slicing the DBObjects? That could happen if you pass by value into a function, or if you store inside a container directly (not through a pointer).

Another thing is why is the tableName() not pure-virtual in your base class?


you could use a 'pure virtual' function to make sure that only subclasses can be used, since the base class (DbObject) doesn't have a table name.

this would force all instantiations of DbObject (through the inherited classes) to have a valid table name

example: virtual QString tableName() = 0;


First, use Google to read about "object slicing" in C++. It's easy (but wrong) to slice an object in C++, especially for novices: slicing happens if you pass an object by value (which is usually wrong) instead of passing by reference (which is usually right), for example if you declare a parameter of type "DBObject" (wrong) instead of "DbObject&" or "const DbObject&" (right).

Second, add the following statements to your DBObject class:

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&);
}; 

Declaring non-default, unimplemented copy and assignment will cause compile-time errors whereveryou try to pass a DBObject by value: so, the third step is to fix these errors by changing the parameter types to pass by reference instead.


You shouldn't inline virtual functions, because some compilers can't handle that very well.

You should move the implementation of DBObject::tableName() and DBUserObject::tableName to the .cpp file.


You don't seem to be doing anything wrong here. Are you sure the problem is not with your QString::arg(...) method?

Explicitly call this->tableName(); looks like a compiler problem.

-- UPDATE --

Actually your definition of tableName() should be

virtual void tableName() const { ... }

Make sure that your operator= for QString is in order (both a const and non-const version), it could be that the QString that is returned from tableName() is a temporary via the stack, in which case the operator= will be called...

0

精彩评论

暂无评论...
验证码 换一张
取 消