代码之家  ›  专栏  ›  技术社区  ›  Rob

交换功能的好处?

  •  30
  • Rob  · 技术社区  · 15 年前

    浏览一些C++问题,我经常看到一个STL友好类应该实现的注释 swap 函数(通常是作为一个朋友)有人能解释它带来了什么好处,STL是如何适应它的,以及为什么这个函数应该作为一个 friend ?

    7 回复  |  直到 14 年前
        1
  •  24
  •   Yacoby    15 年前

    对于大多数类,默认交换是可以的,但是,在所有情况下,默认交换并不是最佳的。最常见的例子是使用 Pointer to Implementation 成语。在默认交换中,大量的内存将被复制,您是专门的交换吗,您只需交换指针就可以显著加快速度。

    如果可能,它不应该是类的朋友,但是它可能需要访问私有数据(例如,原始指针),您的类可能不想在类API中公开这些数据。

        2
  •  23
  •   Thierry Roy    15 年前

    std::swap()的标准版本适用于大多数可分配的类型。

    void std::swap(T& lhs,T& rhs)
    {
        T tmp(lhs);
        lhs = rhs;
        rhs = tmp;
    } 
    

    但它不是最佳实现,因为它调用复制构造函数,然后调用两个赋值运算符。

    通过为类添加自己的std::swap()版本,可以实现swap()的优化版本。

    例如std::vector。上面定义的默认实现将非常昂贵,因为您需要复制整个数据区域。可能会释放旧的数据区域或重新分配数据区域,并为每个复制的项上包含的类型调用复制构造函数。专用版本有一个非常简单的方法来执行std::swap()。

    // NOTE this is not real code.
    // It is just an example to show how much more effecient swaping a vector could
    // be. And how using a temporary for the vector object is not required.
    std::swap(std::vector<T>& lhs,std::vector<T>& rhs)
    {
        std::swap(lhs.data,rhs.data);  // swap a pointer to the data area
        std::swap(lhs.size,rhs.size);  // swap a couple of integers with size info.
        std::swap(lhs.resv,rhs.resv);
    }
    

    因此,如果您的类可以优化swap()操作,那么您应该这样做。否则将使用默认版本。

    我个人喜欢将swap()实现为非抛出成员方法。然后提供std::swap()的专门版本:

    class X
    {
        public:
            // As a side Note:
            //    This is also useful for any non trivial class
            //    Allows the implementation of the assignment operator
            //    using the copy swap idiom.
            void swap(X& rhs) throw (); // No throw exception guarantee
    };
    
    
      // Should be in the same namespace as X.
      // This will allows ADL to find the correct swap when used by objects/functions in
      // other namespaces.
      void swap(X& lhs,X& rhs)
      {
         lhs.swap(rhs);
      } 
    
        3
  •  17
  •   Yacoby    15 年前

    如果您想交换(例如)两个向量而不知道它们的实现,那么基本上您必须这样做:

    typedef std::vector<int> vec;
    
    void myswap(vec &a, vec &b) {
       vec tmp = a;
       a = b;
       b = tmp;
    }
    

    如果 a b 包含许多元素,因为所有这些元素都在 , tmp .

    但是,如果swap函数知道并能够访问向量的内部,那么可能会有一个更有效的实现:

    void std::swap(vec &a, vec &b) {
       // assuming the elements of the vector are actually stored in some memory area
       // pointed to by vec::data
       void *tmp = a.data;
       a.data = b.data;
       b.data = tmp;
       // ...
    }
    

    在这个实现中,只需要复制一些指针,而不是像第一个版本中那样的所有元素。因为这个实现需要访问向量的内部,所以它必须是一个友元函数。

        4
  •  10
  •   Pieter    15 年前

    我把你的问题解释为基本上三个不同的(相关的)问题。

    1. 为什么STL需要交换?
    2. 为什么要实施专门的掉期(I.S.O.依赖于违约 swap )?
    3. 为什么要作为一个朋友来实施呢?

    为什么STL需要交换?

    支持STL的班级需要的原因 掉期 那是 掉期 在许多STL算法中用作原始操作。(例如) reverse , sort , partition 等通常使用 掉期 )

    为什么要实施专门的掉期(I.S.O.依赖于违约 掉期 )?

    这部分问题已经有很多(好的)答案了。基本上,经常了解类的内部结构可以让您编写更优化的 掉期 功能。

    为什么要作为一个朋友来实施呢?

    STL算法总是将交换称为自由函数。所以它需要作为一个非成员函数来使用。
    而且,因为只有当您可以使用内部结构的知识来编写更高效的交换时,编写定制的交换才是有益的,这意味着您的自由函数需要访问类的内部,因此需要一个朋友。

    基本上,它没有 要成为朋友,但如果不需要成为朋友,通常也没有理由实现自定义交换。

    注意,您应该确保自由函数与类在同一个名称空间内,这样STL算法可以通过Koening查找找到您的自由函数。

        5
  •  9
  •   Igor Zevaka    15 年前

    swap函数的另一个用途是帮助异常安全代码: http://www.gotw.ca/gotw/059.htm

        6
  •  2
  •   Alexandre C.    14 年前

    要实现分配运算符:

    class C
    {
        C(C const&);
        void swap(C&) throw();
        C& operator=(C x) { this->swap(x); return *this; }
    };
    

    这是异常安全的,传递值时通过复制构造函数进行复制,传递临时(通过 copy elision )

        7
  •  1
  •   JoeG    15 年前

    效率:

    如果有一个类持有(智能)指向数据的指针,那么交换指针可能比交换实际数据更快——3个指针副本对3个深度副本。

    如果您使用“using std::swap”+一个非限定的交换调用(或者只使用一个限定的boost::swap调用),那么ADL将使用自定义交换函数,允许编写有效的模板代码。


    安全性:

    指针交换(原始指针、std::auto_ptr和std::tr1::shared_ptr)不会引发,因此可以用于实现非引发交换。非抛出交换使编写提供强异常保证(事务性代码)的代码更加容易。

    一般的模式是:

    class MyClass 
    {
      //other members etc...
    
      void method()
      {
        MyClass finalState(*this);//copy the current class
        finalState.f1();//a series of funcion calls that can modify the internal
        finalState.f2();//state of finalState and/or throw.
        finalState.f3();
    
        //this only gets call if no exception is thrown - so either the entire function 
        //completes, or no change is made to the object's state at all.
        swap(*this,finalState);
      }
    };
    

    至于它是否应该作为朋友来实现,交换通常需要了解实现细节。使用一个调用成员函数的非朋友还是使用一个朋友,这是一个品味问题。


    问题:

    自定义交换通常比单个分配快,但单个分配总是比默认的三个分配交换快。如果你想移动一个对象,就不可能用泛型的方式知道交换或赋值是否是最好的——C++ +0x用移动构造函数解决的问题。