我们可以从 Qt 容器继承吗?
Posted
技术标签:
【中文标题】我们可以从 Qt 容器继承吗?【英文标题】:Can we inherit from Qt containers? 【发布时间】:2013-07-29 16:53:08 【问题描述】:我们是否应该能够继承 Qt 容器,例如 QList
、QVector
或 QMap
,以便专门化它们并添加功能?如果是这样,我们需要做什么才能确保容器正常工作(虚拟方法、虚拟析构函数等?)。如果没有,为什么不呢?我还有哪些其他选择?
【问题讨论】:
【参考方案1】:STL 和 Qt 容器都选择非虚拟析构函数。
有一个有趣的讨论为什么会这样,以及为什么它没有被 Qt5 修复。
QList 没有虚拟析构函数,但继承自 http://qt-project.org/forums/viewthread/16416另外,请注意 STL 样式和 Qt 容器之间的进一步差异。在他的博客文章An introduction into 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>
的界面。见Live on Coliru或ideone
众所周知,std::stack
没有模拟顺序容器,因此没有定义begin()
、end()
或operator[]
。通过一些技巧,我们可以定义一个 eDSL 来提供这些功能,而无需组合或继承。
为了真正强调您可以以基本方式“重载”被包装类的行为,我们将这样做,以便您可以将 extend(stack)[n]
的结果隐式转换为 std::string
,即使堆栈包含例如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 ;
;
现在我们要做的就是seed我们的表达式模板。我们将使用包装任何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
以获得更真实的示例。
【讨论】:
不知道构造函数可以是虚拟的。 @Jeffrey 析构函数应该对于任何可以多态使用的类都是虚拟的。否则,通过基类指针删除会导致 UB(泄漏和更糟) 啊 :) 我太快了。固定 那么总体答案是否,对吧?如果是这样,“我还有什么其他选择”? @Jeffrey 那将是我的建议。但是,请参阅 Qt5 在该主题中的细微差别。具体来说,QStringList
是派生自 QList
的类的示例以上是关于我们可以从 Qt 容器继承吗?的主要内容,如果未能解决你的问题,请参考以下文章
我可以从 Qt Creator 向 xcodebuild 传递参数吗?