• QT 中使用 c++ 的指针


    之前没有接触过 c++,不过听说 c++ 的指针很坑,直到最近在用 QT / C++ 写一个 Linux Deepin 系统上检测网络流量和网速的小程序时,发现 c++ 的指针用起来真的特别蛋疼。

    不过好在花了几个小时最终还是明白了指针的用法。

    有一段代码的原型大概是这样的:

    QList<NetFlowObject>  netflowobj_list;
    
    /** 从 list 列表中找出网卡名为 ifname 的 NetFlowObject 对象 **/
    bool getNfoFromList(QString ifname, NetFlowObject &nfo);

    其中 NetFlowObject 是自己写的一个类,QList 是 Qt 提供的一个链表。 getNfoFromList 函数返回 boolean 型结果,如果找到相同名称的网卡,返回 true,并将 nfo 设为 QList 中找到的 NetFlowObject 对象。否则返回 false。

    那么最开始的想法是通过遍历 QList 找到 NetFlowObject 对象。

    bool NetInfo::getNfoFromList(QString ifname,NetFlowObject &nfo) {    //-------- A①

      foreach(NetFlowObject o, netflowobj_list) {              //-------- A②

        if(o.getIfName() == ifname) {  //-------- A③

          nfo = o; return true;

        }

      }

      return false;

    }

    void NetInfo::someFunction() {
       // 如果找到相同的 nfo 对象,修改它的数据
       NetFlowObject nfo1;
       bool finded = getNfoFromList(ifname, nfo1);              //-------- B① 
       if(finded) {
         nfo1.updateRecvBytes(if_recv_bytes.toInt());
         nfo1.updateTransBytes(if_trans_bytes.toInt());
       }
    }

    嗯,上面的这段代码很显然没有办法修改 QList 链表中的对象的属性。首先,函数是传值的,也就是说 A① 处函数的参数 nfo 是不会影响 someFunction 里 B① 处的 nfo1 对象的。nfo1 对象的属性改变同样也不会影响 nfo 对象。

    通过函数的参数传递的只是 nfo1 对象的一个副本,两个对象之间不会影响。

    其次,A②处的 foreach 这个便捷的循环也是提供 QList 对象的一个副本,这样的话,更加没有办法修改找到的 NetFlowObject 对象了。

    好吧,这个错误是 c++ 最常见也是最愚蠢的错误,那么想要得到 QList 中的某个 NetFlowObject 对象的引用,函数传递的就不能是 NetFlowObject 对象了,那么就改成 NetFlowObject * 也就是指针吧。另外,将循环改为 for 计数循环。

    代码如下

    bool NetInfo::getNfoFromList(QString ifname,NetFlowObject *nfo) {
        for(int i = 0; i < netflowobj_list.count(); i++) {
            NetFlowObject o = netflowobj_list[i];
        //        PrintUtil::print(o.getIfName() + "  ===  " + QString::number(o.getLatestRecvBytes()));
            if(o.getIfName() == ifname) {
                *nfo = netflowobj_list[i];  // 将指针所指向的 NetFlowObject 对象修改为 o      -------C①
                return true;
            }
        }
        return false;
    }

    相应的,

    bool finded = getNfoFromList(ifname, &nfo1);

    但是这样做,发现修改 nfo1 对象仍然没有效果, QList 中 NetFlowObject 对象的属性依然没有改变。恩没有错,上面代码 C① 处又是传递的副本。

    这里要说一下 QList 对象的 at(i) 函数和 QList[i] 数组形式得到的对象是不同的。函数原型为 const T &QList.at(int i) const,也就是不能通过 at() 的返回值对 nfo 对象进行修改,

    而数组形式的函数原型是 T &QList::operator[] (int i),也就是用起来和平常的数组没什么太大区别。

    既然一层引用不能达到效果,那么,函数传递的时候,就传一个 NetFlowObject * 对象的指针,也就是作为 NetFlowObject 对象的指针的引用。(这个指针的指针说起来太绕口了,第二个指针改称引用,不然脑子就浆糊了),贴出最终的代码:

    bool NetInfo::getNfoFromList(QString ifname,NetFlowObject **nfo) {
        for(int i = 0; i < netflowobj_list.count(); i++) {
            NetFlowObject o = netflowobj_list[i];
    //        PrintUtil::print(o.getIfName() + "  ===  " + QString::number(o.getLatestRecvBytes()));
            if(o.getIfName() == ifname) {
                *nfo = &netflowobj_list[i];  // 将指针所指向的 NetFlowObject 指针修改为 QList 中第 i 个对象的引用
                return true;
            }
        }
        return false;
    }
    
    void NetInfo::someFunction() {
            NetFlowObject *nfo;
            bool finded = getNfoFromList(ifname, &nfo);
            if(finded) {
                nfo->updateRecvBytes(if_recv_bytes.toInt());
                nfo->updateTransBytes(if_trans_bytes.toInt());
            }
    }    

    通过两层引用,最终达到了修改 QList 链表中对象的目的。

    感觉还有一种解决方法,就是将函数原型 getNfoFromList 修改成 NetFlowObject * getNfroFromList(QString ifname);

    找到相应的对象后直接返回该对象的引用,这样就不用通过函数参数传递两层引用了。

    --------------------------  思考 -------------------------

    引用指针绕的头都晕了,而且这个错误理论上应该属于逻辑错误(编译时不会报错,运行时不会发生异常),但是特喵的就是结果不对,debug 起来也很蛋疼,通过长时间的分析和总结才能得到正确的结果。

    本博客由 BriFuture 原创,并在个人博客(WordPress构建) BriFuture's Blog 上发布。欢迎访问。
    欢迎遵照 CC-BY-NC-SA 协议规定转载,请在正文中标注并保留本人信息。
  • 相关阅读:
    Log4Net的使用之winform
    开源一个跨平台运行的服务插件
    定时管理器框架-Task.MainForm
    nginx+iis+redis+Task.MainForm构建分布式架构 之 (redis存储分布式共享的session及共享session运作流程)
    关于SQL查询效率,100w数据,查询只要1秒
    写的一般,从起源到具体算法-深度学习综述
    如何避免SHRINKDATABASE & SHRINKFILE 产生索引碎片(转载)
    在windows service中启动类型“Automatic” 和 “Automatic (Delayed start)” 有何不同?
    C# 对WinForm应用程序的App.config的加密
    SQL Server中怎么查看每个数据库的日志大小,以及怎么确定数据库的日志文件,怎么用语句收缩日志文件
  • 原文地址:https://www.cnblogs.com/brifuture/p/6037485.html
Copyright © 2020-2023  润新知