我们是否应该能够从 Qt 容器(例如 )继承QList
,QVector
或者QMap
为了专门化它们并添加功能?如果是这样,我们需要做什么才能确保容器正常工作(虚拟方法、虚拟析构函数等?)。如果没有,为什么不呢?我还有什么其他选择?
1 回答
STL 和 Qt 容器都选择非虚拟析构函数。
有一个有趣的讨论为什么会这样,以及为什么它没有被 Qt5 修复。
- QList 没有虚析构函数,但是继承自 http://qt-project.org/forums/viewthread/16416
另外,请注意 STL 样式和 Qt 容器之间的进一步差异。在他的博客文章Qt 简介中引用 Jens Weller 的话:
尽管如此,Qt 容器和 STL 容器之间还是有一个重要区别:Qt 容器具有值语义,并且只会在写入时执行复制,而 std 容器将在复制时复制其全部内容。这种行为解释了大多数 Qt 基类,它们只会在需要时为数据创建一个新实例。在处理 Qt 及其容器时,这种隐含的资源共享是一个非常重要的概念。
您的选择一如既往:
作品
例如
struct MyFancyList { QList<MyType> _data; bool frobnicate() { return true; } };
自由功能
例如用非成员操作扩展 QList:
template <typename> bool frobnicate(QList<T>& list) { // your algorithm return true; }
如果你真的想做一些古怪的事情,比如创建隐式转换或重载成员运算符,你可以求助于表达式模板。
更新:后者也是QStringBuilder
新版本中采用的方法。看
奖金
为了好玩,这里有一个(糟糕!)说明如何使用表达式模板来扩展std::stack<T>
. 在 Coliru或ideone上观看
众所周知,std::stack
没有对顺序容器建模,因此没有begin()
, end()
, 或operator[]
定义。通过一些技巧,我们可以定义一个 eDSL 来提供这些功能,而无需组合或继承。
为了真正强调您可以以基本方式“重载”被包装类的行为,我们将这样做,以便您可以隐式地将结果转换extend(stack)[n]
为 a std::string
,即使堆栈包含 eg 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 };
}
};
}
现在我们要做的就是为我们的表达式模板播种。我们将使用一个包装 any 的辅助函数std::stack
:
template <typename T>
exprtemplates::stack_expr<T> extend(std::stack<T>& s) { return { s }; }
理想情况下,我们的用户永远不会意识到exprtemplates
命名空间中的确切类型:
#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";
}
瞧。更疯狂:
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];
印刷
0.5
0.6
0.7
0.8
免责声明
- 我知道
std::stack
有一个限制性界面是有原因的。 - 我知道我的索引实现具有可怕的效率。
- 我知道隐式转换是邪恶的。这只是一个人为的例子。
- 在现实生活中,使用 Boost::Proto 来实现 DSL。手工完成所有的机制有很多陷阱和陷阱。
寻找QStringBuilder
更真实的生活样本。