PDA

View Full Version : Writing a QVector subclass



Computer Hater
19th June 2011, 17:01
Hi.

I'm trying to write a subclass of QVector, since I want general (Q)Vector functionality, but add a few context specific functionalities.
I tried to write it from scratch, but ran into some problems.

So I had Qt Creator construct a default subclass to QVector instead (see screenshot). I tried it with and without "Inherits QObject" selected ('ssvector.h' and 'ssvector2.h' in the attached files, respectively).

I was surprised that the default code doesn't include any "template" keywords. When I googled for inheritance from template classes the results always said the subclasses should have the "template" statement, too.
But then from the look at the QVector source code, it isn't a template class itself, but has a template member, so I suppose that's okay.

But the default generated code doesn't compile and I get this error in both cases (i.e. with or without QObject inheritance):

error: expected class-name before '{' token

in the line with the { bracket after the "class" statement.
(shorter version code with no QObject inheritance below)


#ifndef SSVECTOR2_H
#define SSVECTOR2_H

#include <QVector>

class SSVector2 : public QVector
{
public:
SSVector2();
};

#endif // SSVECTOR2_H


So my questions are:
What is this error ? Why do I get it with the default generated code ?
and
How do I go about defining a QVector subclass in general ?

Thank you for any hint.

Zlatomir
19th June 2011, 17:11
The problem most likely is with the QVector that doesn't have a parent (so no constructor takes a QObject pointer as a parent) so don't pass a parent to the QVector's constructor.

stampede
19th June 2011, 17:26
QVector is a template class, so your class must be a template too, or inherit from instantiated QVector<Type> class.

edit:
so it should look like:


class MyVector : public QVector<int>{
...
};

or:


template <typename T> class MyVector : public QVector<T>{
...
};

edit2:

But then from the look at the QVector source code, it isn't a template class itself
Then I guess we have different QVector sources:


// from qvector.h
...
template <typename T>
class QVector
{
typedef QVectorTypedData<T> Data;
union { QVectorData *p; QVectorTypedData<T> *d; };

public:
inline QVector() : p(&QVectorData::shared_null) { d->ref.ref(); }
explicit QVector(int size);
...

Yes, it is a template ;)

Computer Hater
19th June 2011, 19:58
Thanks to both of you for your quick responses. Unfortunately, I didn't see them earlier.

@Zlatomir
You're right. QVector doesn't have a parent. I kind of assumed that since Creator showed me that that option it would have one, so I tried both versions, with and without QObject inheritance.
Still, the version without the inheritance had the same problem.

@stampede
Yes, of course you're right, too. QVector is a template class. :-D
I haven't had any practice with template classes and amidst all the trouble-shooting got it mixed up in my head. So when I noticed that Creator didn't default code the subclass as a template and I looked into the QVector source code, I was suddenly feeling unsure and got it all wrong. Sorry for the confusion. :-/

The thing is, I did it just the way you wrote when I coded it the first time around (after some googling on template inheritance). It compiled fine, but then I ran into a problem with trying to define a copy constructor that I couldn't figure out. So I thought I'd try and see what Qt Creator default codes for me, assuming that would be a good starting point since I figured this would have to be correct. Hence the above problem and question.

Now I went back to the original attempt, reduced to the following sample code that shows the problem with trying to use a copy constructor.


ssvector.h



#ifndef SSVECTOR_H
#define SSVECTOR_H

#include <QVector>

template <class T>
class SSVector : public QVector<T>
{
public:
SSVector<T>() {}
SSVector<T>(const SSVector<T>& that) {}
};

#endif // SSVECTOR_H



main.cpp



#include <QDebug>
#include "ssvector.h"

int main(int argc, char *argv[])
{
SSVector<QString> dummy();
SSVector<QString> dummy2(dummy);

return 0;
}


The class itself compiles fine, but when I try to declare any instances in the main program, I get the following error:

[..]/Test/main.cpp:15: error: no matching function for call to 'SSVector<QString>::SSVector(SSVector<QString> (&)())'

[..]/Test/ssvector.h:11: candidates are: SSVector<T>::SSVector(const SSVector<T>&) [with T = QString]

[..]/Test/ssvector.h:10: SSVector<T>::SSVector() [with T = QString]

The line numbers are off because I removed any outcommented text. The error refers to the declaration of "dummy2" though, using the copy constructor.
I maybe also should mention that in the original version I used both a .h and a .cpp file, but reduced it now to a (trivial) in-class definition for brevity. I get the same error in both cases.

I don't understand the first line of the error, in particular what the bracketed ampersand "(&)" and the additional brackets "()" in the end mean, and why it doesn't recognize the given copy constructor as a matching signature.

Again, sorry for the confusion and for only now getting to the actual problem.

stampede
19th June 2011, 20:09
SSVector<QString> dummy();
Because this is declaration of a function called "dummy" taking no parameters and returning SSVector<QString>.
Remove the brackets after "dummy" and it should work:

SSVector<QString> dummy/* no () here*/;
SSVector<QString> dummy2(dummy);

squidge
19th June 2011, 20:36
Last time I played with vector, it didn't have a virtual dtor, and inheritance from a class without a virtual dtor is pure evil (IMO).

Exactly why do you want to subclass? Would it not be better to add functions as per the STL Algorithms? This is typically what I prefer.

Computer Hater
19th June 2011, 21:05
Thank you, stampede !
This solves the problem for the inline definitions of the constructors I used for simplification.
However, if I put the code again in the .cpp file:

ssvector.cpp



#include "ssvector.h"

template <class T> SSVector<T>::SSVector()
{
}

template <class T> SSVector<T>::SSVector(const SSVector<T>& that)
{
}


I now get this error output:

[..]/Test/main.cpp:14: error: undefined reference to `SSVector<QString>::SSVector()'
[..]/Test/main.cpp:15: error: undefined reference to `SSVector<QString>::SSVector(SSVector<QString> const&)'
:-1: error: collect2: ld returned 1 exit status

Which actually was the original error that started me down this awry line of solution attempts that got me into more problems. I suppose I should have just posted the original problem when my attempts to solve it didn't work out, instead of posting subsequent problems with more mistakes in them.
Well, lesson learned. (I hope.)

So, is the template use in the code definition wrong ?
It works out fine if I use non-template inheritance to, say, QChar.

Added after 4 minutes:


Last time I played with vector, it didn't have a virtual dtor, and inheritance from a class without a virtual dtor is pure evil (IMO).

Exactly why do you want to subclass? Would it not be better to add functions as per the STL Algorithms? This is typically what I prefer.

I'm not yet experienced enough to understand why that matters. Didn't have it come up before. I will give it some thought.
To answer your question: I just basically wanted a QVector, with some additional functionality thrown in, so I thought inheritance was the way to go.

stampede
19th June 2011, 21:16
You can't separate this code, because full template definition should be "visible" to the compiler when instantiating the template.
Say, you want to instantiate for integers (lets simplify things):


#include "ssvector.h"
//now everything included in ssvector.h is visible here, that is declarations of constructors

SSVector<int> x;
// compiler now generates the code:
class SSVector_int : public QVector<int> /*some kind of a name is given for this specific class, lets say SSVector_int*/
{
public:
SSVector_int() ;
SSVector_int(const SSVector_int& that) ;
};

// I think its now clear that constructor of SSVector_int cannot be called, because compiler dont know its definition here

If you want you can keep the implementation in separate file, but when you want to use the vector template class, you will have to include both files (so that "implementation" will be visible to the complier when instantiating)
So its up to you how to fix it, I'd simply keep the implementation inline.
Btw. answering squidge's questions could be good too ;)

Computer Hater
19th June 2011, 22:05
Thanks, stampede. That is good to know.
But I still don't understand why there is a difference to a non-template class. You say the compiler automatically generates the given code. So the declarations are available at compile time, right ?
Then you say the constructor cannot be called because the compiler doesn't know its definition. But isn't that the same for any non-template class that I define in a separate .cpp file ?
What am I missing ?

I will read up on the visibility of templates and virtual destructors, as squidge mentioned, then.

stampede
19th June 2011, 22:28
But isn't that the same for any non-template class that I define in a separate .cpp file
No, as long as you compile and link methods from the "separate .cpp file", compiler can "look" into compiled code and take any method it needs. In the template case, there is no code in separate .cpp generated for the specific template instantiation.

So the declarations are available at compile time, right ?
Declarations - yes, definitions - no.

But I still don't understand why there is a difference to a non-template class.
Because when you create non-template class derived from QVector<Type>, compiler has everything it needs - QVector<Type> is instantiated, so its like a normal class from now on.
Main difference is in the .cpp files -
Case 1: template-based approach:


// .cpp file with template methods:
template <class T> SSVector<T>::SSVector()
{}
template <class T> SSVector<T>::SSVector(const SSVector<T>& that)
{}
// compiler has a template, but when you compile this file, it does not "see" that
// somewhere in other files you want to use above methods for a specific T

Case 2: non-template approach:


// .cpp file with non-template methods
#include "ssvector.h"
// include actually pastes the file content, so it looks like:
class SSVector : public QVector<int/*or anything else*/>
{
public:
SSVector();
SSVector(const SSVector& that);
};

SSVector::SSVector()
{}
SSVector(const SSVector& that)
{}
// all is ok, compiler knows what is QVector<int>, and it has both declarations and
// definitions of all needed SSVector methods

I hope it helps.

Computer Hater
19th June 2011, 22:58
Thank you for the effort of explaining it, stampede. :b
I'm afraid I lack some combination of knowledge and the proper conception right now. I hope it will become clearer to me over time.

squidge
20th June 2011, 08:15
I'm not yet experienced enough to understand why that matters. Didn't have it come up before. I will give it some thought.Basically, it is too easy to create bugs and memory leaks by not releasing objects you think are being released. For instance, if you create variables or objects in your constructor or elsewhere, don't assume that your destructor will get called to release them.

If you are just adding methods which are stateless, then there is typically no reason to subclass vector.

But in the end it is your code and you are of course free to code it how you like :)

Computer Hater
20th June 2011, 22:09
I just did a little experiment with inheritance and virtual desctructors and I think I get what you mean.
It seems that QVector still has no virtual destructor, as you said, telling from the result I got. (And from the source code, now that I think of it ;-))
So I think I get the importance of that part. Thank you for the warning, the relevance of which I missed the first time around.
For this and other reasons, I think I will use a non-inherited template class instead.

If I may trouble you some more, could you give me a quick idea of what a "stateless method" is ?
I'm not familiar with the term. I googled it, but without knowing more context, it's hard to read the right hits or use the right search terms.

squidge
20th June 2011, 22:37
a "Stateless method" is just that - a method which retains no state between calls. For example, if you subclassed QVector to add a "find" method rather than just using find of std::algorithm (ie, std::find(x.begin(), x.end(), v) != x.end()) that would be a stateless method as no information (state) is stored between calls.