交换功能的好处?

时间:2010-01-04 10:39:44

标签: c++ stl swap

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

7 个答案:

答案 0 :(得分:25)

对于大多数类,默认交换很好,但是,默认交换在所有情况下都不是最佳的。最常见的例子是使用Pointer to Implementation成语的类。与默认交换一样,大量内存会被复制,如果是专门的交换,只需交换指针就可以显着加快速度。

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

答案 1 :(得分:23)

标准版本的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);
  } 

答案 2 :(得分:17)

如果你想交换(例如)两个向量而不了解它们的实现,你基本上必须做这样的事情:

typedef std::vector<int> vec;

void myswap(vec &a, vec &b) {
   vec tmp = a;
   a = b;
   b = tmp;
}

如果ab包含许多元素,则效率不高,因为所有这些元素都是在abtmp之间复制的。

但是如果交换函数知道并且可以访问向量的内部,那么可能有更高效的实现:

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;
   // ...
}

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

答案 3 :(得分:10)

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

  1. 为什么STL需要交换?
  2. 为什么要实施专门的交换(i.s.o.依赖于默认的swap)?
  3. 为什么要将它作为朋友实施?
  4.   

    为什么STL需要交换?

    STL友好类需要swap的原因是swap在许多STL算法中用作基本操作。 (例如reversesortpartition等通常使用swap实现

      

    为什么要实施专门的交换(i.s.o.依赖于默认的swap)?

    这部分问题已经有很多(好的)答案。基本上,了解类的内部经常允许您编写更加优化的swap函数。

      

    为什么要将它作为朋友实施?

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

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

    请注意,您应确保free函数与您的类位于同一名称空间内,以便STL算法可以通过Koening查找找到您的自由函数。

答案 4 :(得分:9)

交换功能的另一个用途是帮助例外安全代码:http://www.gotw.ca/gotw/059.htm

答案 5 :(得分:2)

实现赋值运算符:

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

这是异常安全的,复制是在传递值时通过复制构造函数完成的,并且当您传递临时(通过copy elision)时编译器可以优化副本。

答案 6 :(得分:1)

效率:

如果你有一个拥有(智能)数据指针的类,那么交换指针可能比交换实际数据更快--3个指针拷贝与3个深拷贝。

如果你使用'using std :: swap'+对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用移动构造函数解决的问题。