我们可以继承Qt容器吗?

时间:2013-07-29 16:53:08

标签: c++ qt containers

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

1 个答案:

答案 0 :(得分:6)

STL和Qt Containers都选择非虚拟析构函数。

有一个有趣的讨论为什么会这样,以及为什么它没有用Qt5修复。

另外,请注意STL样式和Qt容器之间的进一步差异。在他的博客文章An introduction into Qt中引用Jens Weller:

  

但是,Qt容器和STL容器之间存在重要区别:Qt容器具有值语义,并且仅在写入时执行复制,而std容器在复制时将复制其完整内容。这个行为占据了大多数Qt基类,它们只在需要时才为数据创建一个新实例。在处理Qt及其容器时,这种隐含的资源共享是一个非常重要的概念。

您的选择一如既往:

  • 组合物

    E.g。

     struct MyFancyList
     { 
            QList<MyType> _data;
    
            bool frobnicate() { return true; }
     };
    
  • 免费功能

    E.g。使用非成员操作扩展QList:

    template <typename> bool frobnicate(QList<T>& list)
    {
         // your algorithm
         return true;
    }
    

如果您真的想做一些有趣的事情,比如创建隐式转换或重载成员运算符,您可以使用表达式模板。

更新:后者也是QStringBuilder在新版本中采用的方法。参见

加成

为了好玩,这里有一个(坏的)插图,说明如何使用表达式模板来扩展std::stack<T>的界面。见Live on Coliruideone

众所周知,std::stack不会为顺序容器建模,因此没有begin()end()或{{1定义。有点hackery,我们可以定义一个eDSL来提供这些功能,而不需要组合或继承。

为了让我们能够以重要方式“重载”被包装类的行为,我们会做到这一点,以便您可以隐式地将operator[]的结果转换为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 }; } }; }

的辅助函数
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];

<强>免责声明

  1. 我知道 0.5 0.6 0.7 0.8 有一个限制性的界面是有原因的。
  2. 我知道我的索引实现具有可怕的效率。
  3. 我知道隐含的转换是邪恶的。这只是一个人为的例子。
  4. 在现实生活中,使用Boost :: Proto来获取DSL。手工完成所有技术有许多陷阱和陷阱。
  5. 查看std::stack更真实的样本。