Преимущества функции подкачки?

Просматривание некоторых вопросов о C++, я часто видел комментарии, что благоприятный для STL класс должен реализовать a swap функция (обычно как друг.) Может кто-то объяснять, что извлекает выгоду, это приносит, как STL вписывается в это и почему эта функция должна быть реализована как a friend?

31
задан Rob 4 January 2010 в 10:39
поделиться

7 ответов

Для большинства классов своп по умолчанию является нормальным, однако, своп по умолчанию не является оптимальным во всех случаях. Наиболее распространенным примером этого был бы класс, использующий идиому Pointer to Implementation . Там, где, как и в случае с swap по умолчанию, будет скопирован большой объем памяти, является специализированным swap, вы могли бы значительно ускорить его, только поменяв указатели.

Если возможно, он не должен быть другом класса, однако ему может понадобиться доступ к приватным данным (например, к сырым указателям), которые вы, вероятно, не захотите выставлять в классе API.

25
ответ дан 27 November 2019 в 21:39
поделиться

Эффективность:

Если у вас есть класс, который содержит (smart) указатели на данные, то скорее всего, обмен указателями быстрее, чем фактическими данными - 3 копии указателей против 3 глубоких копий.

Если вы используете 'использование std::swap' + неквалифицированный вызов для обмена (или просто квалифицированный вызов для повышения::swap), то ADL подхватит пользовательскую функцию обмена, позволяющую написать эффективный код шаблона.


Safety:

Pointer swaps (сырые указатели, std::auto_ptr и std::tr1::shared_ptr) не бросают, поэтому могут быть использованы для реализации не-бросающего swap. Небрасывающий своп облегчает написание кода, который обеспечивает сильную гарантию исключений (транзакционный код).

Общий шаблон:

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 решает с помощью конструкторов перемещения

.
1
ответ дан 27 November 2019 в 21:39
поделиться

Еще одно использование функции подкачки - это поддержка кода безопасности исключений: http://www.gotw.ca/gotw/059.htm

9
ответ дан 27 November 2019 в 21:39
поделиться

Если вы хотите поменять (например) два вектора, ничего не зная об их реализации, то, по сути, вы должны сделать что-то вроде этого:

typedef std::vector<int> vec;

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

Это не эффективно, если a и b содержат много элементов, так как все эти элементы скопированы между a, b и tmp.

Но если бы функция подкачки знала о внутренностях вектора и имела доступ к ним, то, возможно, была бы более эффективная реализация:

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

В этой реализации нужно скопировать всего несколько указателей, а не все элементы, как в первой версии. А поскольку эта реализация нуждается в доступе к внутреннему содержимому вектора, она должна быть функцией друга.

.
17
ответ дан 27 November 2019 в 21:39
поделиться

Я истолковал ваш вопрос как в основном три различных (связанных) вопроса.

  1. Зачем STL нужен обмен?
  2. Зачем реализовывать специализированный обмен (т.е. полагаться на обмен по умолчанию swap)?
  3. Почему он должен быть реализован как друг?

Почему STL нужен обмен?

Причина, по которой классу STL нужен swap, заключается в том, что swap используется в качестве примитивной операции во многих алгоритмах STL. (например, обратный , сортировка , раздел и т.д. обычно реализуются с использованием swap)

Почему должен быть реализован специализированный swap (т.е. полагаться на swap по умолчанию по умолчанию )?

На эту часть Вашего вопроса уже есть много (хороших) ответов. В основном, знание внутренней структуры класса часто позволяет Вам написать намного более оптимизированную функцию swap.

Почему она должна быть реализована как "друг"?

Алгоритмы STL всегда будут вызывать swap как бесплатную функцию. Поэтому он должен быть доступен как функция, не являющаяся членом, чтобы быть полезной.
И, поскольку написание персонализированного свопа выгодно только тогда, когда вы можете использовать знания о внутренних структурах для написания более эффективного свопа, это означает, что ваша свободная функция будет нуждаться в доступе к внутренним ресурсам вашего класса, а значит и в друге.

В основном, она не имеет друга, но если ей не нужно быть другом, то обычно нет причин и для реализации пользовательского подкачки.

Обратите внимание, что вы должны убедиться, что свободная функция находится в том же пространстве имён, что и ваш класс, так что алгоритмы STL могут найти вашу свободную функцию с помощью поиска Конинга.

.
10
ответ дан 27 November 2019 в 21:39
поделиться

Стандартная версия 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);
  } 
23
ответ дан 27 November 2019 в 21:39
поделиться

Для реализации операторов присваивания:

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

Это безопасно для исключений, копирование выполняется через конструктор копирования при передаче по значению, и копирование может быть оптимизировано компилятором при передаче временного значения (через copy elision).

2
ответ дан 27 November 2019 в 21:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: