Почему не вызывается ни оператор конструктора, ни оператор присваивания? [Дубликат]

Я всегда думаю об этом как «проход по копии». Это копия значения, будь она примитивной или справочной. Если это примитив, это копия битов, которые являются значением, и если это объект, то это копия ссылки.

public class PassByCopy{
    public static void changeName(Dog d){
        d.name = "Fido";
    }
    public static void main(String[] args){
        Dog d = new Dog("Maxx");
        System.out.println("name= "+ d.name);
        changeName(d);
        System.out.println("name= "+ d.name);
    }
}
class Dog{
    public String name;
    public Dog(String s){
        this.name = s;
    }
}

вывод java PassByCopy:

< blockquote>

name = Maxx name = Fido

Примитивные классы-оболочки и строки неизменяемы, поэтому любой пример с использованием этих типов не будет работать так же, как другие типы / объекты.

278
задан Community 23 May 2017 в 12:03
поделиться

4 ответа

Введение

Технический обзор - пропустить этот ответ .

Для распространенных случаев, когда происходит копирование - пропустить этот ответ .

Копирование elision - это оптимизация, выполняемая большинством компиляторов для предотвращения дополнительных (потенциально дорогостоящих) копий в определенных ситуациях. Он делает возвращение по значению или пропускной способностью практически осуществимым (применяются ограничения).

Это единственная форма оптимизации, которая исключает (ha!) Правило as-if - копирование может применяться даже если копирование / перемещение объекта имеет побочные эффекты.

Следующий пример, взятый из Wikipedia :

struct C {
  C() {}
  C(const C&) { std::cout << "A copy was made.\n"; }
};

C f() {
  return C();
}

int main() {
  std::cout << "Hello World!\n";
  C obj = f();
}

В зависимости от компилятора & amp; все допустимые значения:

Hello World! Была сделана копия. Была сделана копия.


Hello World! Копия была сделана.


Hello World!

Это также означает, что можно создавать меньше объектов, t полагаться на определенное количество вызываемых деструкторов. Вы не должны иметь критической логики внутри copy / move-constructors или destructors, так как вы не можете полагаться на их вызываемые.

Если вызов конструктора копирования или перемещения отменяется, этот конструктор должен все же существуют и должны быть доступны. Это гарантирует, что копирование не позволяет копировать объекты, которые обычно не могут быть скопированы, например. потому что у них есть частный или удаленный конструктор copy / move.

C ++ 17: Начиная с C ++ 17, Copy Elision гарантируется, когда объект возвращается напрямую:

struct C {
  C() {}
  C(const C&) { std::cout << "A copy was made.\n"; }
};

C f() {
  return C(); //Definitely performs copy elision
}
C g() {
    C c;
    return c; //Maybe performs copy elision
}

int main() {
  std::cout << "Hello World!\n";
  C obj = f(); //Copy constructor isn't called
}
161
ответ дан Jorge Perez 16 August 2018 в 11:23
поделиться
  • 1
    не могли бы вы объяснить, когда произойдет второй выход, и когда 3-й? – zhangxaochen 19 June 2014 в 15:00
  • 2
    @zhangxaochen, когда и как компилятор решает оптимизировать этот путь. – Luchian Grigore 19 June 2014 в 16:11
  • 3
    @zhangxaochen, 1-й вывод: копия 1 находится от возврата к temp и копирует 2 из temp в obj; 2-е место, когда один из вышеперечисленных вариантов оптимизирован, вероятно, копия reutnr устранена; трое оба лишены – victor 7 November 2014 в 17:06
  • 4
    Хм, но, на мой взгляд, это ДОЛЖНО быть функцией, на которую мы можем положиться. Потому что, если мы не можем, это серьезно повлияет на то, как мы реализуем наши функции в современном C ++ (RVO vs std :: move). Во время просмотра некоторых видеороликов CppCon 2014 у меня действительно сложилось впечатление, что все современные компиляторы всегда делают RVO. Кроме того, я где-то читал, что без каких-либо оптимизаций компиляторы применяют его. Но, конечно, я не уверен в этом. Вот почему я спрашиваю. – j00hi 5 February 2015 в 09:02
  • 5
    @ j00hi: никогда не записывайте move в оператор return - если rvo не применяется, возвращаемое значение по умолчанию все равно выведено. – MikeMB 10 March 2015 в 02:32

Общие формы копирования elision

Для технического обзора - пропустить этот ответ .

Для менее технического вида & amp; введение - пропустить этот ответ .

(Именован) Оптимизация возвращаемого значения является распространенной формой копирования. Это относится к ситуации, когда объект, возвращаемый значением из метода, имеет свою копию. Пример, приведенный в стандарте, иллюстрирует именованную оптимизацию возвращаемого значения, так как объект назван.

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  Thing t;
  return t;
}
Thing t2 = f();

Оптимизация регулярного значения возвращается, когда возвращается время:

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  return Thing();
}
Thing t2 = f();

Другие распространенные места, где происходит копирование, - это когда время передается по значению:

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
void foo(Thing t);

foo(Thing());

или когда исключение выбрано и поймано по значению:

struct Thing{
  Thing();
  Thing(const Thing&);
};

void foo() {
  Thing c;
  throw c;
}

int main() {
  try {
    foo();
  }
  catch(Thing c) {  
  }             
}

Общие ограничения для копирования:

  • множественные точки возврата
  • условная инициализация

Большинство компиляторов коммерческого уровня поддерживают copy elision & amp; (N) RVO (в зависимости от настроек оптимизации).

73
ответ дан Community 16 August 2018 в 11:23
поделиться
  • 1
    Мне было бы интересно увидеть «Общие ограничения», пункты пули объяснены немного ... что делает эти ограничивающие факторы? – phonetagger 16 January 2013 в 19:54
  • 2
    @phonetagger Я связан с статьей msdn, надеюсь, что очистит некоторые вещи. – Luchian Grigore 16 January 2013 в 21:11

Копирование elision - это метод оптимизации компилятора, который устраняет ненужное копирование / перемещение объектов.

В следующих случаях компилятору разрешено пропускать операции копирования / перемещения и, следовательно, не вызывать связанный конструктор:

  1. NRVO (Именованная оптимизация возвращаемого значения): If функция возвращает тип класса по значению, а выражение оператора return - это имя энергонезависимого объекта с автоматическим временем хранения (который не является параметром функции), а затем копирование / перемещение, которое будет выполняться не оптимизирующим компилятор может быть опущен. Если это так, возвращаемое значение создается непосредственно в хранилище, к которому в противном случае возвращаемое значение функции было бы перемещено или скопировано.
  2. RVO (Оптимизация возвращаемого значения): если функция возвращает неназванный временный объект, который будет перемещен или скопирован в место назначения наивным компилятором, копия или перемещение могут быть опущены в соответствии с 1.
#include <iostream>  
using namespace std;

class ABC  
{  
public:   
    const char *a;  
    ABC()  
     { cout<<"Constructor"<<endl; }  
    ABC(const char *ptr)  
     { cout<<"Constructor"<<endl; }  
    ABC(ABC  &obj)  
     { cout<<"copy constructor"<<endl;}  
    ABC(ABC&& obj)  
    { cout<<"Move constructor"<<endl; }  
    ~ABC()  
    { cout<<"Destructor"<<endl; }  
};

ABC fun123()  
{ ABC obj; return obj; }  

ABC xyz123()  
{  return ABC(); }  

int main()  
{  
    ABC abc;  
    ABC obj1(fun123());//NRVO  
    ABC obj2(xyz123());//NRVO  
    ABC xyz = "Stack Overflow";//RVO  
    return 0;  
}

**Output without -fno-elide-constructors**  
root@ajay-PC:/home/ajay/c++# ./a.out   
Constructor    
Constructor  
Constructor  
Constructor  
Destructor  
Destructor  
Destructor  
Destructor  

**Output with -fno-elide-constructors**  
root@ajay-PC:/home/ajay/c++# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors    
root@ajay-PC:/home/ajay/c++# ./a.out   
Constructor  
Constructor  
Move constructor  
Destructor  
Move constructor  
Destructor  
Constructor  
Move constructor  
Destructor  
Move constructor  
Destructor  
Constructor  
Move constructor  
Destructor  
Destructor  
Destructor  
Destructor  
Destructor  

Даже когда происходит копирование и конструктор copy / move (как будто никакой оптимизации вообще не было), в противном случае программа плохо сформирована.

Вы должны разрешить такое копирование только в тех местах, где это не повлияет наблюдаемое поведение вашего программного обеспечения. Копирование elision - единственная форма оптимизации, которая позволяет иметь (то есть elide) наблюдаемые побочные эффекты. Пример:

#include <iostream>     
int n = 0;    
class ABC     
{  public:  
 ABC(int) {}    
 ABC(const ABC& a) { ++n; } // the copy constructor has a visible side effect    
};                     // it modifies an object with static storage duration    

int main()   
{  
  ABC c1(21); // direct-initialization, calls C::C(42)  
  ABC c2 = ABC(21); // copy-initialization, calls C::C( C(42) )  

  std::cout << n << std::endl; // prints 0 if the copy was elided, 1 otherwise
  return 0;  
}

Output without -fno-elide-constructors  
root@ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp  
root@ajay-PC:/home/ayadav# ./a.out   
0

Output with -fno-elide-constructors  
root@ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors  
root@ajay-PC:/home/ayadav# ./a.out   
1

GCC предоставляет возможность -fno-elide-constructors отключить копирование. Если вы хотите избежать возможного копирования, используйте -fno-elide-constructors.

Теперь почти все компиляторы обеспечивают копирование при разрешении оптимизации (и если для его отключения не установлен другой параметр).

Заключение

С каждым экземпляром копии исключается одна конструкция и одно совпадение с уничтожением копии, что позволяет сэкономить время процессора, а один объект не создан, что позволяет сэкономить место в стеке кадр.

34
ответ дан underscore_d 16 August 2018 в 11:23
поделиться
  • 1
    утверждение ABC obj2(xyz123()); - это NRVO или RVO? не получает ли временная переменная / объект такой же, как ABC xyz = "Stack Overflow";//RVO – Asif Mushtaq 28 August 2015 в 02:45
  • 2
    Грамматика и форматирование в этом посте оставляют желать лучшего. Я надеюсь, что кто бы ни просмотрел мои предложенные правки, согласен с тем, что это делает этот взгляд более похожим на сообщение, которое получает 11 upvotes. – underscore_d 27 June 2016 в 19:53
  • 3
    Чтобы получить более конкретную иллюстрацию RVO, вы можете обратиться к сборке, которую генерирует компилятор (изменить флаг компилятора -fno-elide-constructors, чтобы увидеть diff). godbolt.org/g/Y2KcdH – Gab是好人 3 December 2016 в 17:18
76
ответ дан Community 6 September 2018 в 05:37
поделиться
Другие вопросы по тегам:

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