做爱动态图

V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
James369
V2EX  ›  C++

在使用某些类库的时候,有时传引用,有时传指针,这有什么设计原则吗?

  •  
  •   James369 · 34 天前 · 1568 次点击
    这是一个创建于 34 天前的主题,其中的信息可能已经有所发展或是发生改变。
    比如说,这 2 个 Http 的 post 方法:
    QNetworkReply *post(const QNetworkRequest &request, QIODevice *data);
    QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data);
    一个 data 传指针,一个传引用。

    另外,我发现一个有趣现象,就是使用栈上的变量,最好传引用,传指针可能会导致运行出错。
    比如像这样:
    QIODevice dev;
    ... //dev 操作
    auto reply = manager->post(request, &dev); //栈上的 dev 会出现段错误,而如果是 new QIODevice 再传指针就没事。

    而这种情况,直接用栈上的变量却又没事。
    QByteArray data;
    ... //data 操作
    auto reply = manager->post(request, data); //一切正常
    第 1 条附言  ·  34 天前
    纠正一下,不是 QIODevice 类,应该是 QHttpMultiPart 类,使用的方法是:
    QNetworkReply *post(const QNetworkRequest &request, QHttpMultiPart *multiPart);
    22 条回复    2021-02-08 02:23:51 +08:00
    James369
        1
    James369   34 天前
    另外,顺便问下怎么贴漂亮的 有颜色高亮的代码
    across
        2
    across   34 天前 via iPhone
    引用不可为空。
    且一般编码时你知道确切的创建释放期,就像你说的,栈上函数下调用方便。
    指针就自由度更高点了
    James369
        3
    James369   34 天前
    不能编辑了,我贴完整一点:

    //传指针的情况,栈上的 dev 可能会出现段错误
    QNetworkRepley *post1(QNetworkAccessManager& manager) {
    QIODevice dev;
    ...
    auto reply = manager->post(request, &dev); //栈上的 dev 可能会出现段错误,如果是 new QIODevice 传指针就没事。
    return reply;
    }

    //传引用的情况,栈上的 data 运行正常:
    QNetworkRepley *post2(QNetworkAccessManager& manager) {
    QByteArray data;
    ...
    auto reply = manager->post(request, data); //一切正常
    return reply;
    }
    laminux29
        4
    laminux29   34 天前
    初学 Cpp,建议多写 Test,多在 Debug 下跑跑,多看看变量值。

    class TestClass
    {
    public:
    int intValue;
    std::string stringValue;
    };

    int main()
    {
    TestClass x1;
    TestClass * x2 = new TestClass();
    断点,展开 x1 与 x2,对比一下。
    }
    wutiantong
        5
    wutiantong   34 天前
    一般不存在你说的这种堆 /栈区别,
    我查了一下,QIODevice 是个抽象基类,是不能直接构建的。
    那么无论是 QIODevice dev; 还是 new QIODevice 都应该要报错的才对。
    要不你再检查一下?
    mxT52CRuqR6o5
        6
    mxT52CRuqR6o5   34 天前
    传引用可以看做传指针的语法糖
    XiaoxiaoPu
        7
    XiaoxiaoPu   34 天前
    跟参数在栈还是堆没关系,纯粹是这两个函数对参数的不同要求。

    *data)
    Sends an HTTP POST request to the destination specified by request and returns a new QNetworkReply object opened for reading that will contain the reply sent by the server. The contents of the data device will be uploaded to the server.

    data must be open for reading and must remain valid until the finished() signal is emitted for this reply.

    注意这一行。第二个函数没有这个要求。


    James369
        8
    James369   34 天前
    @wutiantong 你说得对,是我搞错了,应该是 QHttpMultiPart 类。 调用的方法是:QNetworkReply *post(const QNetworkRequest &request, QHttpMultiPart *multiPart);
    James369
        9
    James369   34 天前
    @XiaoxiaoPu 那这个参数可以设计成 传引用吗? 为什么
    wutiantong
        10
    wutiantong   34 天前
    @James369

    看起来,之所以是指针( QHttpMultiPart *或 QIODevice *)的原因在于这些类型都是 QObject 的子类,相对的 QNetworkRequest 和 QByteArray 就不是继承自 QObject

    QT 里的 QObject 这套设计应该是很早期的,非常类似于 Objective-C 里的 NSObject,为了与 C 兼容会选择用指针类型来表达对象。

    脱离开 QObject 的类型就更接近于现代 C++,一般是不鼓励用指针传参的,而应该用引用。
    fffang
        11
    fffang   34 天前
    写 OC 的表示不知道什么叫引用
    wutiantong
        12
    wutiantong   34 天前
    @wutiantong

    再提一下,基于我对 Objective-C 的了解,如果 QObject 和 NSObject 是差不多的话,那么它的各种子类,都应该在堆上动态分配内存(这一步会涉及到引用计数的内存管理,很可能 QObject 重载了 new 方法)

    所以,你确实不应该在栈上直接构建一个 QObject 的子类对象。
    wutiantong
        13
    wutiantong   34 天前
    @James369

    所以也正是因为 QObject 的子类都必须要在堆上构建,它们才会被声明成指针类型的格式。
    James369
        14
    James369   34 天前
    @wutiantong 我之所以会在栈上构建对象,主要因为懒,懒的考虑对象的释放和管理问题,哈哈
    wutiantong
        15
    wutiantong   34 天前 via Android   ❤️ 1
    @James369 你可以理解为参数声明为指针类型就是在提醒你这个参数必须要在堆上构建
    YUCOAT
        16
    YUCOAT   34 天前
    引用和指针的一个区别是,存在空指针,但是不存在空引用。

    比如,你设计一个函数 LoadFail:
    bool LoadFile(const FilePath& path, FailReason* reason = nullptr);
    你不希望别人传空 path 进来,这时你可以使用引用。
    但是,reason 可以为空,这时 reason 可以使用指针。
    heyf2015
        17
    heyf2015   33 天前
    传引用必须带着 const,不带 const 时都传指针。基本是这样的约定
    GeruzoniAnsasu
        18
    GeruzoniAnsasu   33 天前
    一般来说,如果接口更接近 C 风格,尤其是跨语言 /跨文件(如动态库)调用时,使用 C 风格并且传指针的麻烦要少得多。 对于 c++ 代码内部来说,引用带有强类型限定,并且可以通过 RAII 管理生命周期,传引用能比较容易避免编译期(代码写得不好)的问题,如悬垂指针、UAF 、资源泄露等等

    不过上面提到了 QT 的设计特例我想多补充一点,QT 的 QObject 默认构造必须带有一个 parent 指针,用于在析构 QObject 时链式释放所有的子对象,这其实有点类似于 IOC,将本对象的生命周期交给自己的容器对象也就是 parent 来管理。 而要想实现 IOC 所需的对象容器,显然对象不可能临时构造在栈上。这也许是使用指针并且总是从堆上构造对象的另一个角度的原因。
    uranuswch
        19
    uranuswch   33 天前
    前司的 code style 要求:
    传入参数 const reference,传出参数 指针
    Wirbelwind
        20
    Wirbelwind   33 天前
    指针引用看你习惯
    edimetia3d
        21
    edimetia3d   33 天前
    .. 印象中很多书都说过这个问题啊, 主要是 style 的问题, 技术层面没有什么优劣, 引用底层也是指针.


    函数传参中, 如果不用拷贝传值, 那么只能用`const T &`或者 `T *`
    `const T &`用于向用户表明: 我一定不会修改传入的对象.
    `T *`用于向用户表明: 我会(极有可能)修改传入的对象.

    从调用方看也能直观的看出来, `func(obj1,&obj2)`,`obj1`要么是 const 引入传入的,要么是拷贝传入,所以一定不会被修改.
    `&obj2`则很有可能在函数 return 后被修改.

    Google Style 应该也是这样的.
    intlinfo
        22
    intlinfo   21 天前
    指针自由度高,引用并非不可为空,在设计上引用也可以为 nil

    两者都是指向同一内存地址,只是 指针是 -> 引用是 .

    在多线程的情况下,引用直接引用原有地址。

    基础指针引用原有地址,c++11 智能指针传参时会涉及到复制。

    具体多谢 test 自行测试观察为好。要讲理论我不行嘿嘿,但是实际上写代码还是会注意这些问题的,经验积累的。

    拒绝脑残面试官让你讲理论。 :)
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   4017 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 04:15 · PVG 12:15 · LAX 20:15 · JFK 23:15
    ♥ Do have faith in what you're doing.