问题
Are we supposed to be able to inherit from Qt containers such as QList, QVector or QMap in order to specialize them and add functionality? If so, what do we need to do in order to be sure that the container works correctly (virtual methods, virtual destructor etc..?). If not, why not and what other options do I have?
回答1:
Both, STL and Qt Containers opt for non virtual destructors.
There is an interesting discussion why this is, and why its not fixed with Qt5.
- QList has no virtual destructor, but is inherited from http://qt-project.org/forums/viewthread/16416
Also, note further differences between STL style and Qt containers. Quoting Jens Weller in his Blog post An introduction into Qt:
Still, there is an important difference between Qt containers and STL containers: Qt containers have value semantics, and will only perform copy on write, while a std container will copy its full contents when copied. This behavoir accounts for most of Qt base classes, that they will only create a new instance for data, when needed. This implicit sharing of resources is a very important concept to understand when dealing with Qt and its containers.
Your options are as always:
composition
E.g.
struct MyFancyList { QList<MyType> _data; bool frobnicate() { return true; } };free functions
E.g. extend QList with non-member operations:
template <typename> bool frobnicate(QList<T>& list) { // your algorithm return true; }
If you really wanted to do funcky stuff, like create an implicit conversion or overload a member operator, you could resort to expression templates.
Update: the latter is also the approach taken by QStringBuilder in new versions. See
- Lecture: Expression Templates (video, slides) by Volker Krause
Bonus
For fun, here's a (bad!) illustration of how you could use expression templates to extend the interface of std::stack<T>. See it Live on Coliru or ideone
As we all know, std::stack doesn't model a sequential container, and therefore doesn't have begin(), end(), or operator[] defined. With a bit of hackery, we can define a eDSL to provide these features, without composition or inheritance.
To really drive the point home that you can 'overload' behaviour of the wrapped class in essential ways, we'll make it so that you can implicitly convert the result of extend(stack)[n] to a std::string, even if the stack contains e.g. int.
#include <string>
#include <stack>
#include <stdexcept>
namespace exprtemplates
{
template <typename T> struct stack_indexer_expr
{
typedef std::stack<T> S;
S& s;
std::size_t n;
stack_indexer_expr(S& s, std::size_t n) : s(s), n(n) {}
operator T() const {
auto i = s.size()-n; // reverse index
for (auto clone = s; !clone.empty(); clone.pop())
if (0 == --i) return clone.top();
throw std::range_error("index out of bounds in stack_indexer_expr");
}
operator std::string() const {
// TODO use `boost::lexical_cast` to allow wider range of T
return std::to_string(this->operator T());
}
};
template <typename T> struct stack_expr
{
typedef std::stack<T> S;
S& s;
stack_expr(S& s) : s(s) {}
stack_indexer_expr<T> operator[](std::size_t n) const {
return { s, n };
}
};
}
Now all we have to do is seed our expression templates. We'll use a helper function that wraps any std::stack:
template <typename T>
exprtemplates::stack_expr<T> extend(std::stack<T>& s) { return { s }; }
Ideally, our users never realize the exact types inside exprtemplates namespace:
#include <iostream>
int main()
{
std::stack<double> s;
s.push(0.5);
s.push(0.6);
s.push(0.7);
s.push(0.8);
std::string demo = extend(s)[3];
std::cout << demo << "\n";
}
Voila. More craziness:
auto magic = extend(s);
std::cout << magic[0] << "\n";
std::cout << magic[1] << "\n";
std::cout << magic[2] << "\n";
std::cout << magic[3] << "\n";
double as_double = magic[0];
std::string as_string = magic[0];
Prints
0.5
0.6
0.7
0.8
DISCLAIMERS
- I know
std::stackhas a restrictive interface for a reason. - I know that my indexing implementation has horrific efficiency.
- I know that implicit conversions are evil. This is just a contrived example.
- In real life, use Boost::Proto to get a DSL going. There are many pitfalls and gotchas in doing all the mechanics by hand.
Look at QStringBuilder for a more real life sample.
来源:https://stackoverflow.com/questions/17929735/can-we-inherit-from-qt-containers