Это даст вам некоторое представление о том, как Java действительно работает до такой степени, что в следующем обсуждении о передаче Java по ссылке или передаче по значению вы просто будете улыбаться: -)
Шаг один, пожалуйста, удалите из вашего разума это слово, которое начинается с «p» «_ _ _ _ _ _ _», особенно если вы исходите из других языков программирования. Java и «p» не могут быть записаны в одной книге, форуме или даже в txt.
Шаг два помнит, что при передаче объекта в метод, по которому вы передаете ссылку Object, а не сам объект
Теперь подумайте о том, что ссылка / переменная объекта делает /:
В следующем (пожалуйста, не пытайтесь скомпилировать / выполнить это ...):
1. Person person;
2. person = new Person("Tom");
3. changeName(person);
4.
5. //I didn't use Person person below as an argument to be nice
6. static void changeName(Person anotherReferenceToTheSamePersonObject) {
7. anotherReferenceToTheSamePersonObject.setName("Jerry");
8. }
Что происходит?
Изображение стоит тысячи слов:
[/g12]
Обратите внимание, что стрелки anotherReferenceToTheSamePersonObject направлены к объекту а не к переменному человеку!
Если вы этого не поняли, просто доверьтесь мне и помните, что лучше сказать, что Java проходит по значению. Ну, пройдем по эталонному значению. Ну, еще лучше pass-by-copy-of-the-variable-value! ;)
Теперь не стесняйтесь меня ненавидеть, но обратите внимание, что при этом нет разницы между передачей примитивных типов данных и объектов при обсуждении аргументов метода.
Вы всегда передайте копию битов значения ссылки!
Java - это пропускная способность, потому что внутри метода вы можете измените ссылочный объект столько, сколько хотите, но как бы вы ни старались, вы никогда не сможете изменить переданную переменную, которая будет поддерживать привязку (а не p _ _ _ _ _ _ _) того же объекта независимо от того, что!
blockquote>
Вышеуказанная функция changeName никогда не сможет изменить фактическое содержимое (битовые значения) переданной ссылки. В другом слове changeName не может заставить Person лицо ссылаться на другой Object.
blockquote>
Конечно, вы можете сократить его и просто сказать, что Java является передачей по значению!
Технический обзор - пропустить этот ответ .
Для распространенных случаев, когда происходит копирование - пропустить этот ответ .
Копирование 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 }
Для технического обзора - пропустить этот ответ .
Для менее технического вида & 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 (в зависимости от настроек оптимизации).
Копирование elision - это метод оптимизации компилятора, который устраняет ненужное копирование / перемещение объектов.
В следующих случаях компилятору разрешено пропускать операции копирования / перемещения и, следовательно, не вызывать связанный конструктор:
#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
.
Теперь почти все компиляторы обеспечивают копирование при разрешении оптимизации (и если для его отключения не установлен другой параметр).
С каждым экземпляром копии исключается одна конструкция и одно совпадение с уничтожением копии, что позволяет сэкономить время процессора, а один объект не создан, что позволяет сэкономить место в стеке кадр.
ABC obj2(xyz123());
- это NRVO или RVO? не получает ли временная переменная / объект такой же, как ABC xyz = "Stack Overflow";//RVO
– Asif Mushtaq
28 August 2015 в 02:45