【问题标题】:Appending onto QList<QFile*>附加到 QList<QFile*>
【发布时间】:2014-02-28 02:21:04
【问题描述】:

我想知道为什么当你制作 QFIle 的 QList 时,你必须让它们成为指针。例如我有一个类有一个 QList:

class Files
{
public:
   void AddFile(QString newFile);

private:
QList<QFile*> files;
}

现在当调用 AddFIle 时,它​​会这样做:

QFile* newt = new QFile(new_File);
files += newf;

为什么我无法将 QList 更改为 QList?每次我尝试这样做时都会出错:

QFIle::QFile(const QFile&) 是私有的

这是我将代码更改为 QList 时的样子:

class Files
{
public:
   void AddFile(QString newFile);

private:
QList<QFile> files;
}

然后是.cpp

QFile newt(new_File);
files += newf;

【问题讨论】:

    标签: c++ qt raii


    【解决方案1】:

    QFile 类型必须在您的示例中用作*QFile,因为作者将复制构造函数设为私有。 QList 类型需要访问其模板参数的复制构造函数。 QFile 的复制构造函数无法访问,但QFile* 类型具有可访问的复制构造函数。

    这很可能是故意的。复制QFile 对象可能是不安全的。

    【讨论】:

      【解决方案2】:

      简短的回答:因为复制 QFile 对象很危险,所以作者无法复制(通过将复制构造函数设为私有)。

      长答案:

      1. Qt 对 QObjects 的内部处理:大多数重要的 Qt 对象都是堆生成的,many of them 具有特殊的内部变量,如果使用得当,它们的内存(以及生命周期和引用)可以在内部进行管理通过 Qt。所以处理 Qt 对象的首选方式是作为指针。 Qt 提供多个smart pointers 方便使用。

      2. 一般的资源处理:另一个原因是,即使没有 Qt 的内部结构,在不使用句柄的情况下共享资源对象的副本也是一个坏主意。

      【讨论】:

      • 复制 QFile 对象并不危险。这样做是不可能的,因为该对象的作者禁止这样做。如果 QObject、QIODevice、QFileDevice 和 QFile 被设计为可复制的,那很好,但无论如何它都没什么用。
      • @KubaOber 是的,这在设计上是不可能的......复制资源对象和管理不善的危险是因为你有多个副本,这就是它被设计成不可能的原因。编辑。而且它不仅没用 - 从记忆和一致性的角度来看,它可能是非常危险的。
      • @Fox 只有当你把它设计成危险的时候才会有危险。旨在复制的资源对象不应该管理不善,它应该“正常工作”。它可以被设计成完全安全但完全无用,因为对于这种每个人都会本能地同意的行为,确实没有理智的语义。因此,几乎每个人都会以与他们对行为的期望不一致的方式使用它,因此是错误的。在 QObject 层次结构的情况下,它是由软件设计的人为因素驱动的设计决策。完全没有技术原因。
      • 所以,您可以查看几个心智模型: 1. 我有一个资源对象,我可以将一个句柄传递给多个对象,所以如果我从一个句柄关闭它(我挥手并发和其他同时性危险),幸运的是它刷新了“中心”对象,并且可以从其他句柄中看到。 2. 我实现了复制语义并检查每个资源属性,并考虑当 其中一个 更改时 original.property 应该如何与 copy.property 相关联。我提到它很危险,只是因为#2 有多种心智模型,并且在它们之间切换会使事情变得混乱
      • 简而言之,如果副本只是无用,我会非常好。拥有多个对象代表(而不是引用)一个具有可变状态的单一资源的陷阱太多了......并且与现实世界的类比关系很差。与预期相比,实现必然具有-偏斜-不一致的心理模型,因此很危险。
      【解决方案3】:

      这真的很简单:QList 有点“不足”,因为它只支持可复制的对象。 QObjects 是不可复制的。 QFile 是一个 QObject。仅此而已。

      C++11 的 std::list 支持 QObject

      如果您使用 C++11 并且有一个支持就地构造并且不需要复制或默认构造它所拥有的对象的容器,那么您可以当然将对象实例存储在它。以下适用于 Qt 4 和 Qt 5:

      #include <QFile>
      #include <QDir>
      #include <list>
      
      int main()
      {
         std::list<QFile> files;
         files.emplace_back(QDir::homePath() + QDir::separator() + "test.txt");
         files.front().open(QIODevice::WriteOnly);
         files.front().write("abcdef\n");
      }
      

      为什么 QFile 不可复制?

      想想 QObject 必须做什么,然后想想副本的语义是什么。确保您已经考虑了线程以及信号和插槽。然后,您会意识到几乎每个想要复制 QObject 的人都会想出不同的语义来说明此类 QObject 副本的预期操作。因此它实际上是无用的,因为人们通常会忽略文档,并且因此会出现大量错误。我了解自己,因为我曾经修改过自己的 Qt 副本以允许 QObject 复制。最终结果证明,除了最微不足道的情况外,它在任何情况下都完全适得其反。现在不要忘记,我们只触及了 QObject 复制的语义。

      不要忘记 QFile 是 QIODevice,因此它具有内部缓冲区。现在告诉我你期望这段代码会产生什么:

      QFile foo("file");
      foo.open(QIODevice::WriteOnly);
      foo.write("foo");
      QFile bar(foo);
      bar.write("bar");
      bar.close();
      foo.close();
      

      现在假设您更改了close() 调用的顺序:

      bar.close();
      foo.close();
      

      这只是为了写作。现在假设我们有这样的代码:

      QTcpSocket socket;
      socket.connectToHost("localhost", 8080);
      socket.waitForConnected(); // do not use it in production code!!
      QTcpSocket socket2(socket);
      qDebug() << socket.read(3);
      qDebug() << socket2.read(3);
      

      鉴于连接的另一端已发送“abcdef”,您期望什么 输出是什么?

      换句话说:您不希望能够复制 QIODevice。

      【讨论】:

        猜你喜欢
        • 2018-03-31
        • 2013-07-04
        • 2014-10-05
        • 2019-04-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-25
        • 1970-01-01
        相关资源
        最近更新 更多