C++ and virtual methods overriding
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 DBObject
s? 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...
精彩评论