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

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

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

    想问一下 https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom/3279550#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 就是成员函数了啊
    tianshilei1992
        11
    tianshilei1992  
       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) 这种样子。
    namehao
        17
    namehao  
       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 也谢谢您
    namehao
        20
    namehao  
       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   ·   实用小工具   ·   1227 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 17:55 · PVG 01:55 · LAX 09:55 · JFK 12:55
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.