我们可以从 Qt 容器继承吗?

Posted

技术标签:

【中文标题】我们可以从 Qt 容器继承吗?【英文标题】:Can we inherit from Qt containers? 【发布时间】:2013-07-29 16:53:08 【问题描述】:

我们是否应该能够继承 Qt 容器,例如 QListQVectorQMap,以便专门化它们并添加功能?如果是这样,我们需要做什么才能确保容器正常工作(虚拟方法、虚拟析构函数等?)。如果没有,为什么不呢?我还有哪些其他选择?

【问题讨论】:

【参考方案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在新版本中采用的做法。见

Lecture: Expression Templates (video, slides) 作者:Volker Krause

奖金

为了好玩,这里有一个(糟糕!)说明如何使用表达式模板来扩展std::stack&lt;T&gt; 的界面。见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 传递参数吗?

qt glsl渲染rgb

您可以拥有与您继承的基类相同的类的私有成员吗?

我可以继承 QEvent 吗?

我们可以在 AWS ECS docker 容器上挂载 EFS 吗?

什么是最适合 QAbstractListModel 和 QListView 的 Qt 容器