【问题标题】:when there is performance benefit for writing own swap() function / method当编写自己的 swap() 函数/方法有性能优势时
【发布时间】:2015-09-24 09:11:54
【问题描述】:

假设我有这样的课程:

struct A{
  std::string a;
  std::string b;
  std::string c;
  std::string d;
};

如果我使用std::swap,它可能会这样做:

// pseudo-code:
void std::swap(A &a, A &b){
   A tmp = std::move(a);
   a = std::move(b);
   b = std::move(tmp);
}

它将使用默认的 c-tor 构造“空”对象tmp - 通常是便宜的操作。然后它希望移动 3 次,除非在疯狂的情况下移动衰减到复制。

但是,如果我自己进行交换:

void swap(A &a, A &b){
   std::swap(a.a, b.a);
   std::swap(a.b, b.b);
   std::swap(a.c, b.c);
   std::swap(a.d, b.d);
}

它肯定会使用更少的内存,但它仍然需要构造空std::string - 4次!!!

我可以发狂,用单个 std::string 来实现。

在所有情况下,它看起来都不是很大的改进。

我能想到的唯一合适的情况是默认 c-tor 是否昂贵得离谱。我说的对吗?

【问题讨论】:

  • 您没有调用默认构造函数。您正在调用 move 构造函数。

标签: c++ swap


【解决方案1】:

你不能说你需要或永远不应该创建自己的swap,这完全取决于上下文。在您的示例中,这很可能是不必要的,但您的类的逻辑可能更复杂。

假设您有一个类,其中包含指向某个数据结构的指针,以及与该结构相关的许多指标,每个指标都需要很长时间来计算,并且需要大量空间来存储,而这些指标在对数据进行一些计算时用作临时对象(我知道下面的示例可能是一个糟糕的设计类,但它应该说明这个想法):

class MyClass
{
public:
   void doSomeWork()
   {
       //uses _metricsOneValue and _metricsTwoValue as temporaries,
       //calls other functions that use them as temporaries too, etc.
   }

private:
   //used only in doSomeWork and functions called by it.
   //Value overwritten each call.
   SomeBigClass _metricsOneValue; 
   SomeBigClass _metricsTwoValue;
   <...>
   SomeOtherClass * _data;
}

现在假设您需要swap() 这个类的两个实例。最直接的实现将复制包括旧指标在内的所有内容,实际上,目前不需要这些指标,因为无论如何它们将在您下次调用doSomeWork() 时被覆盖。所以在这种情况下,您可以通过交换指向数据结构的指针来优化swap

【讨论】:

    【解决方案2】:

    我希望一个理智的std::swap(std::string&amp;, std::string&amp;) 实现可以在没有任何临时性的情况下进行交换。它应该只是能够交换指针,尽管我承认小字符串优化可能会在某些实现的特定工作中抛出一个扳手。

    您始终可以使用std::string::swap 成员函数,即I'd expect even more to make use of such a delightful "optimisation"。我的意思是,否则,它的意义何在? :)

    void swap(A &a, A &b)
    {
       a.a.swap(b.a);
       a.b.swap(b.b);
       a.c.swap(b.c);
       a.d.swap(b.d);
    }
    

    您将需要实现这个无论如何,即使它只是移动构造函数的一部分,否则默认的std::swap(A&amp;, A&amp;) 实现将无能为力。

    总之,是的,您应该编写自己的swap 函数,是的,您应该使用它来调用标准交换功能。因为两者都需要,所以没有性能比较。

    【讨论】:

      【解决方案3】:

      实现您自己的swap1 函数的一个好处是在 赋值运算符 中管理/传输动态分配的内存,通过使用 复制和交换习语。

      如果你有以下课程:

      #include <algorithm> // std::copy
      #include <cstddef> // std::size_t
      
      class MyArray{
      public:
          // (default) constructor
         MyArray(std::size_t size = 0) 
             : mSize(size), mArray(mSize ? new int[mSize]() : 0)
         { }
         // copy-constructor
         MyArray(const MyArray& other) 
             : mSize(other.mSize), mArray(mSize ? new int[mSize] : 0),
         { std::copy(other.mArray, other.mArray + mSize, mArray); }
         // destructor
         ~MyArray(){ delete [] mArray; }
      
      private:
          std::size_t mSize;
          int* mArray;
      };
      

      1。实现赋值运算符

      代替:

      MyArray& operator=(const MyArray& other){
         if (this != &other){
          // get the new data ready before we replace the old
          std::size_t newSize = other.mSize;
          int* newArray = newSize ? new int[newSize]() : 0;  
          std::copy(other.mArray, other.mArray + newSize, newArray);  
          // replace the old data  
          delete [] mArray;
          mSize = newSize;
          mArray = newArray;
      }
      
      return *this;
      } 
      

      你可以这样做:

      MyArray& operator=(MyArray other){
          swap(*this, other);  
          return *this;
      } 
      

      2。安全地交换类成员:

      friend void swap(MyArray& first, MyArray& second){
          using std::swap; 
          // by swapping the members of two classes,
          // the two classes are effectively swapped
          swap(first.mSize, second.mSize); 
          swap(first.mArray, second.mArray);
      }
      

      注意:此答案的见解来自this


      1 交换函数是一个非抛出函数,它交换一个类的两个对象,成员对成员。我们可能会想使用std::swap 而不是提供我们自己的,但这是不可能的; std::swap 在其实现中使用复制构造函数和复制赋值运算符,我们最终会尝试根据自身定义赋值运算符!

      【讨论】:

        猜你喜欢
        • 2017-05-08
        • 2021-10-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-14
        • 2014-09-17
        • 1970-01-01
        • 2015-01-06
        相关资源
        最近更新 更多