UnityContainer.Resolve или ServiceLocator.GetInstance?

В простых (практических) терминах:

Копирование объекта означает копирование его «статических» членов и вызов оператора new для его динамических объектов. Правильно?

class A
{
   int i, *p;

public:
   A(const A& a) : i(a.i), p(new int(*a.p)) {}
   ~A() { delete p; }
};

Однако для перемещения объекта (повторяю, с практической точки зрения) подразумевается только копирование указателей на динамические объекты, а не создание новых.

Но разве это не опасно? Конечно, вы можете разрушить динамический объект дважды (ошибка сегментации). Поэтому, чтобы этого избежать, вы должны «недействить» указатели источника, чтобы избежать их разрушения дважды:

class A
{
   int i, *p;

public:
   // Movement of an object inside a copy constructor.
   A(const A& a) : i(a.i), p(a.p)
   {
     a.p = nullptr; // pointer invalidated.
   }

   ~A() { delete p; }
   // Deleting NULL, 0 or nullptr (address 0x0) is safe. 
};

Хорошо, но если я перемещаю объект, исходный объект становится бесполезным, нет? Конечно, но в некоторых ситуациях это очень полезно. Наиболее очевидным является то, что я вызываю функцию с анонимным объектом (временным, rvalue объектом, ..., вы можете называть его разными именами):

void heavyFunction(HeavyType());

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

Это приводит к концепции ссылки «rvalue». Они существуют в C ++ 11 только для того, чтобы определить, является ли полученный объект анонимным или нет. Я думаю, вы уже знаете, что «lvalue» является назначаемым объектом (левая часть оператора =), поэтому вам нужна именованная ссылка на объект, способный действовать как lvalue. Rvalue - это точно противоположное, объект без названных ссылок. Из-за этого анонимный объект и rvalue являются синонимами. Итак:

class A
{
   int i, *p;

public:
   // Copy
   A(const A& a) : i(a.i), p(new int(*a.p)) {}

   // Movement (&& means "rvalue reference to")
   A(A&& a) : i(a.i), p(a.p)
   {
      a.p = nullptr;
   }

   ~A() { delete p; }
};

В этом случае, когда объект типа A должен быть «скопирован», компилятор создает ссылку на lvalue или ссылку на rvalue в соответствии с именем или именем переданного объекта , Когда нет, ваш конструктор move вызывается, и вы знаете, что объект является временным, и вы можете перемещать его динамические объекты, а не копировать их, экономя пространство и память.

Важно помнить, что «статические» объекты всегда копируются. Невозможно «переместить» статический объект (объект в стеке, а не в кучу). Таким образом, различие «move» / «copy», когда объект не имеет динамических членов (прямо или косвенно), не имеет значения.

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

class Heavy
{
   bool b_moved;
   // staff

public:
   A(const A& a) { /* definition */ }
   A(A&& a) : // initialization list
   {
      a.b_moved = true;
   }

   ~A() { if (!b_moved) /* destruct object */ }
};

Итак, ваш код короче (вам не нужно делать nullptr для каждого динамического элемента) и более общий.

Другой типичный вопрос: в чем разница между A&& и const A&&? Конечно, в первом случае вы можете изменить объект, а во втором - нет, но практический смысл? Во втором случае вы не можете его модифицировать, поэтому у вас нет способов сделать недействительным объект (кроме как с изменяемым флагом или что-то в этом роде), и нет никакой практической разницы с конструктором копирования.

И что отличное переадресация? Важно знать, что «ссылка на rvalue» является ссылкой на именованный объект в «области вызова». Но в фактическом объеме ссылка rvalue является именем объекта, поэтому он действует как именованный объект. Если вы передаете ссылку rvalue на другую функцию, вы передаете именованный объект, поэтому объект не принимается как временный объект.

void some_function(A&& a)
{
   other_function(a);
}

Объект a будет скопирован в фактический параметр other_function. Если вы хотите, чтобы объект a продолжал обрабатываться как временный объект, вы должны использовать функцию std::move:

other_function(std::move(a));

С помощью этой строки std::move передаст a значение rvalue и other_function получит объект как неназванный объект. Конечно, если other_function не имеет специфической перегрузки для работы с неназванными объектами, это различие не имеет значения.

Это идеальная пересылка? Нет, но мы очень близки. Идеальная пересылка полезна только для работы с шаблонами, с целью сказать: если мне нужно передать объект другой функции, мне нужно, чтобы, если я получаю именованный объект, объект передается как именованный объект, а когда нет, Я хочу передать его как неназванный объект:

template<typename T>
void some_function(T&& a)
{
   other_function(std::forward<T>(a));
}

Это подпись прототипической функции, которая использует совершенную пересылку, реализованную на C ++ 11 с помощью std::forward. Эта функция использует некоторые правила создания экземпляра шаблона:

 `A& && == A&`
 `A&& && == A&&`

Итак, если T является ссылкой lvalue на A (T = A & amp;), a также (A & amp; & amp; & amp; & amp; ; => A & amp;). Если T является ссылкой rvalue на A, a также (A & amp; & amp; & amp; => A & amp; & amp;). В обоих случаях a является именованным объектом в действительной области, но T содержит информацию о его «ссылочном типе» с точки зрения области вызова. Эта информация (T) передается как параметр шаблона в forward, а «a» перемещается или нет в соответствии с типом T.

13
задан Nikhil Agrawal 4 September 2012 в 18:34
поделиться