关于 cpp 的 copy-and-swap idiom 的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
alanlian
V2EX    C++

关于 cpp 的 copy-and-swap idiom 的问题

  •  1
     
  •   alanlian 2019-09-12 22:57:37 +08:00 4955 次点击
    这是一个创建于 2285 天前的主题,其中的信息可能已经有所发展或是发生改变。

    想问一下 https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom/327950#3279550 这个链接里的这个下面这段 public: friend 是怎么理解的呢? po 主尽力看了原回答给的链接,有些没有看懂。

    class dumb_array { public: friend void swap(dumb_array& first, dumb_array& second) // nothrow { // enable ADL (not necessary in our case, but good practice) using std::swap; // by swapping the members of two objects, // the two objects are effectively swapped swap(first.mSize, second.mSize); swap(first.mArray, second.mArray); } }; 
    27 条回复    2019-09-13 17:41:00 +08:00
    ysc3839
        1
    ysc3839  
       2019-09-12 23:27:26 +08:00 via Android
    因为 mSize 和 mArray 是 private 的,所以需要声明成 friend 才能访问。
    alanlian
        2
    alanlian  
    OP
       2019-09-13 00:01:12 +08:00
    @ysc3839 应该不是这个原因吧?我去掉 friend 还是可以编译成功
    Tony042
        3
    Tony042  
       2019-09-13 00:01:29 +08:00
    同意楼上的,另外 friend 声明写在 public 上面吧,还有 declaration 和 definition 最好分开写,写在一起很乱
    alanlian
        4
    alanlian  
    OP
       2019-09-13 00:05:15 +08:00
    @Tony042 emm 这个只是一个小 sample,是我给的链接里的回答者拿来说明 copy-and-swap idiom 这个问题的,他好像是特意把 friend 写在那个位置的?可以看一下我给的链接
    ysc3839
        5
    ysc3839  
       2019-09-13 00:05:25 +08:00 via Android
    @alanlian 发代码看看?
    psuwgipgf
        6
    psuwgipgf  
       2019-09-13 00:05:26 +08:00
    @Tony042 friend 写在哪里都行,因为他不是类的成员,不过一般是写在开头或结尾
    alanlian
        7
    alanlian  
    OP
       2019-09-13 00:08:22 +08:00
    @ysc3839 可以看下我给的链接,在 A successful solution 这一节,我是直接 copy 里面的代码的
    secondwtq
        8
    secondwtq  
       2019-09-13 01:15:56 +08:00
    friend 好像是只能写在这吧 ...
    这里 swap 是个成员函数,成员函数是可以不加 friend 访问自己的 private 成员的

    之前好像没见过这么写 copy-and-swap idiom 的(不过 C++11 之后好像不用这个 idiom 也可以了
    secondwtq
        9
    secondwtq  
       2019-09-13 01:24:00 +08:00
    啊等等我好像搞混了,无视掉 #8 的评论吧 ... 我再看看
    cyyzero
        10
    cyyzero  
       2019-09-13 01:57:39 +08:00 via Android   1
    没有 friend 就是成员函数了啊
    shiltian
        11
    shiltian  
       2019-09-13 07:40:25 +08:00
    那段代码应该这样写才是直观的:
    class dumb_array {
    public:
    friend void swap(dumb_array& first, dumb_array& second);
    };

    void swap(dumb_array& first, dumb_array& second) {
    ...
    }
    类里面的那个函数是“声明”,下面的那一段才是真正的“定义”。因为在函数的实现里面用到了类的私有成员,因此就必须得将这个函数在类里面声明成 friend 才可以。
    ysc3839
        12
    ysc3839  
       2019-09-13 08:44:47 +08:00 via Android
    @alanlian 我要的是你的“我去掉 friend 还是可以编译成功”的代码。链接中的代码是有 firend 的。
    alanlian
        13
    alanlian  
    OP
       2019-09-13 10:50:03 +08:00
    @ysc3839 https://paste.ubuntu.com/p/C9bNY8XBJV/ 大约是这样的,你看有什么问题麽?
    alanlian
        14
    alanlian  
    OP
       2019-09-13 10:59:22 +08:00
    @cyyzero 惭愧了,直接做一个成员函数会有什么问题么?
    alanlian
        15
    alanlian  
    OP
       2019-09-13 10:59:53 +08:00
    @tianshilei1992 但是为什么不可以直接作为一个成员函数呢?
    ysc3839
        16
    ysc3839  
       2019-09-13 11:01:34 +08:00 via Android   1
    @alanlian 因为没有在类外部调用 swap,所以没问题吧。
    代码中给出的用法是 std::swap 这样单独一个函数的 https://en.cppreference.com/w/cpp/algorithm/swap

    你不写 friend 的话就变成类中的函数了,要使用的话会变成 a.swap(a, b) 这种样子。
    nost
        17
    nost  
       2019-09-13 11:39:53 +08:00   2
    不加 friend 是可以编译成功的,这样的 swap 是类的一个成员函数,对于 operator=这样的操作也是没有问题的。

    但是对于如果在类的外部调写这样的代码。比如在 main 函数中写:dumb_array a, b; std::swap(a, b);那么在 c++11 中是 c=move(a),a = move(b), b = move(c)这样的操作,因为 dumb_array 没有写移动构造和移动赋值函数,就导致会调用拷贝构造函数。

    这样导致的问题就是,swap 本来可以直接将类内的数组指针值进行替换,却多构造了一个临时数组,来进行换值操作。

    当把 swap()写成 friend 之后,swap 就成了 std::swap()的一个重载函数,当在 main 中进行 std::swap(a,b)调用的时候,调用到的就是作为 friend 函数的 swap,也就是 std::swap()的定制版。这样就是直接将类内的指针进行交换,而不用进行额外的拷贝构造函数的调用。

    不知这样,讲明白了没有。
    alanlian
        18
    alanlian  
    OP
       2019-09-13 12:14:20 +08:00
    @namehao 谢谢您,明白了
    alanlian
        19
    alanlian  
    OP
       2019-09-13 12:14:58 +08:00
    @ysc3839 也谢谢您
    nost
        20
    nost  
       2019-09-13 13:14:38 +08:00 via iPhone
    @alanlian 不客气
    secondwtq
        21
    secondwtq  
       2019-09-13 15:31:00 +08:00 via iPad
    这个不是重载的 std::swap,这个新函数应该是通过 ADL 被找到的
    alanlian
        22
    alanlian  
    OP
       2019-09-13 16:12:36 +08:00
    @secondwtq https://stackoverflow.com/questions/11562/how-to-overload-stdswap 这个链接里的这个巨巨也是用的 overload 这个词呢?您看下
    secondwtq
        23
    secondwtq  
       2019-09-13 17:18:04 +08:00   1
    @alanlian 意思类似于:
    Q:传输大文件用什么服务最吼?
    A:顺丰快递
    你感受一下

    标准对 overload 的定义:“When two or more different declarations are specified for a single name **in the same scope**, that name is said to be overloaded.”
    technically,你在使用 swap 的时候用了 overload resolution 的机制,overload resolution 在决定 candidate set 的时候会考虑 scope 和 ADL。不过 overload resolution 和 overloading 是两回事
    alanlian
        24
    alanlian  
    OP
       2019-09-13 17:33:51 +08:00
    @secondwtq 这样子,我大概明白了,谢谢您了
    secondwtq
        25
    secondwtq  
       2019-09-13 17:37:23 +08:00
    其实应该说,你正确调用 swap 的时候,把 std::swap 重载了
    另外 C++20 之前调 std::swap 应该是不会调到你的 swap 去的
    baixiangcpp
        26
    baixiangcpp  
       2019-09-13 17:38:27 +08:00
    《 EFFective C++》肯定没好好看
    baixiangcpp
        27
    baixiangcpp  
       2019-09-13 17:41:00 +08:00
    接上条,没写完就发了) 里边专门有个条款,就讲了 swap,虽然《 C++ primer 》也提过一嘴,但是一笔带过了.....
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     877 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 87ms UTC 18:32 PVG 02:32 LAX 10:32 JFK 13:32
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86