https://chogyujin-study.tistory.com/76
: 임시객체를 반환해서 rvo가 적용되도록 하자.
: 필요에 이해 컴파일러가 만드는 임시 메모리 공간.
- 객체를 값으로 반환할 경우.
- 객체를 값으로 매개변수로 사용할 경우.
- 생성자를 함수 호출하는 것처럼 하면 생성됨.
- 전역 객체를 값으로 반환하더라도 임시객체가 생성된다..
일반적인 객체를 만드는 것이 아닌, 객체명 제외하고, 생성자를 호출하는 식으로 작성하자.
Point(1,2);
중요한 부분은 간접적으로 호출되는 임시객체!!
객체 반환, 객체 인자로 전달할 때 생성된다는 것임.
: 문장의 끝인 세미콜론의 끝에서 소멸됨. // 함수의 블록이 아닌.
Point(1,2)로 임시 객체를 만든후, x y 값을 Print 함수로 출력하라. 이어서 세미 콜론 앞에 콤마 작성후, cout << "main " << endl 을 작성하라.
- 출력 결과를 보면, Point(1,2) 임시 객체가 pint() 함수 호출후 소멸되는 것이 아니라, main 출력 후, 즉 세미콜론 이후에 소멸되는 것을 확인할 수있다.
: 클래스의 객체명을 만들지 않음.
-> 생성자를 직접적으로 함수 호출하듯이 만들면 됨.
-> 위의 예제를 통해 임시객체의 수명을 알 수 있음.
-> 위의 예제를 통해 임시객체를 만드는 방법을 알 수 있음.
1. 임시 객체는 lvalue가 될수 없다. 즉 대입 불가
: 임시객체는 메모리에 지속적으로 존재하는 것이 아니라, 세미콜론 이후에
사라지는 객체이기 때문에 아래의 예시코드처럼 대입이 불가하다.
2. 메모리 주소를 가질 수 없음.
: 일반적인 생각으로는 불가하다고 생각한다.
하지만 엄청난 혼란을 가지고 오는 방법이다. 아래의 설명과 코드를 보자.
메모리 값을 구할 수 있지만, 포인터에 임시객체를 넣는 동작은 혼동을 가지고 온다.
: 아래 코드를 보면, 8번 줄의 delete 를 하기도 전에 이미 6번줄의
; 세미콜론을 마치고서 소멸됨을 확인할 수 있다.
-> 굉장히 혼란스럽다 ..
여기서 우리는 당연히 포인터니까, delete를 해야 한다.
그래서 delete p2;를 해보면....
-> 엑!!! 이런 젠장!!
: 포인터 객체에 임시객체를 넣지 말자.
포인터에는 반드시 lvalue 와 같이 주소있는 객체를 참조해야 한다.
프로그래머의 생각과는 다르게 동작한다.
3. 임시객체는 lvalue 참조 불가함.
-> 실체가 없기 때문에 당연히 불가하다.
4. 임시객체는 const 참조는 가능함. -> 이때는 상수성을 가지게 됨.
밑의 예제 참고.
5. 임시객체는 rvalue 참조가 가능함.
임시 객체는 소멸적인 존재이나, const 참조 객체 또는 const 변수에 대입시
생명이 const 참조 객체 또는 const 참조 변수의 생명으로 연장됨.
그러나 이 때는 상수성을 가지므로, 값 변경이 불가.
임시 객체는 또 rvalue 참조를 통해 수명이 연장되고, 이 때 rvalue 객체는
lvalue로 결정된다. 수정이 가능한 존재가 되어 버린다.
위의 내용을 토대로 포인터에다가 임시객체를 넣으면 안된다.
0) 포인터로 참조하자.
: 가능하지만, 해지해보면, 오류 발생 -> 위에 예시코드 있음.
가) 임시객체의 public 멤버 데이터에 값을 대입해보자.
나) lvalue 참조 객체로 임시객체를 참조하자.
: 이유 -> lvalue 참조 객체는 반드시 메모리 계속 존재하는것만 참조가 가능하기 때문임.
다) rvalue 참조 객체로 임시객체를 대입하라.
: rValue 참조는 상수성이 없음!
라) const 참조 객체로 임시객체를 대입하라.
: 상수성이 추가됨.
함수에 객체를 인자로 보낼 경우, 객체의 수명이 어떻게 되는지 알아보자.
-> 위의 코드를 보면, const& 이기 때문에 임시객체는 생성되지 않음을
알 수 있따.
임시 객체를 함수 인자로 보낼 경우, 함수 호출 완료 후, 객체는 소멸됨.
만약에 객체가 함수의 인자에서만 사용되는 용도라고 한다면,
객체를 만들지 않고, 임시객체로 인자로 보내는 것이 효율적임.
왜냐하면 수명을 호출된 이후까지 가져갈 필요없이, 함수내에서 수명을 마치게 하는 것이 메모리 측면에서 도움이 되기 때문임.
그런데 이 때도 알아야 할 점이 임시 객체를 받는 인자의 타입은
적어도 const & 타입이어야 한다. 또는 && rvalue Ref로 받아야 한다.
-> 위의 그림을 보면, const& 객체이기 때문에 print 함수 호출이 불가하다.
예~~전에 const 객체와 const 함수에 대해서 설명했는데,
여기서 확인해볼수 있는 코드이다.
가) 객체를 함수 인자로 보내고, 다음 행에 cout << "hello" 출력하라.
나) 임시객체를 함수 인자로 보내고, 다음 행에 cout << "hello" 출력하라.
Point p1;
foo(p1);
foo(Point());
cout << "Wow";
- 알아야 할점은 인자 타입도 중요함.
1) value 일 경우, 복사 발생
-> 위 코드를 호출하면 4번의 소멸자
2) &일 경우, lvalue 만 받을 수 있음.
-> lvalue 만 받을 수 있음.
3) && 일 경우, rvalue , 임시객체만 받기 때문에, 복사 발생 안함.
-> rvalue 만 받을 수 있음.
4) const Point& 일 경우,
-> lvalue , rValue 다 받을수 있고, 이때는 2번의 소멸자 호출.
함수의 인자를 일반 객체로 선언하면, 임시객체가 생성되는 것 처럼.
c++ 에서는 객체를 값 타입으로 반환하면, 임시객체가 생성됨.
가) objcect 클래스를 만들고, 대입 연산자. 복사 생성자를 만들자, 디폴트 ,
소멸자도 만들자.
그리고 func()이라는 함수 내부에서 객체를 만들고, 값 타입으로 반환하고,
그리고 Object obj = func() 하자.
객체를 값반환한다고 한다면, 차라리 임시객체를 보내버리자!
그러면 함수에서의 암시적 형변환으로 인한 복사 호출
-> 임시 객체가 안 만들어짐.
그리고 받는 곳에서는 선언과 동시에 대입하도록 해서
대입 연산자 호출되지 않게 하자.
그리고 위의 object 예시의 경우, 최적화설정이 안되어 있어서 copy가 이루어진 것 같다.
: 선언된 객체가 임시객체 반환함수를 대입연산자으로 받으면, 컴파일러가 임시객체 생성 없이 해당 객체를 직접 초기화하게 하는 최적화 방법.
알아야 한 점이 반환하는 함수에서는 객체 선언 없이
임시객체 호출한 것을 바로 반환해야 함!
: standard library p.58
: 사용할 수 있는 복사 생성자나 이동 생성자를 제공한다면, 컴파일러는 복사를 생략하고, 반환값 최적화를 제공한다.
그런데??? 복사 생성자 없어도 소멸자 하나만 호출되는 것을 보니 반환값 최적화 되었다.
객체 반환할때 그냥 값으로 반환하자.
: 컴파일러가 rvo ,nrvo 처리를 알아서 한다.
해당 예시가 적합.
: 컴파일러가 알아서 rvo 처리하는 것을 확인할 수 있다. 240716
원래 대로 라면, 아래와 같이 복사가 나와야 한다.
https://dydtjr1128.github.io/cpp/2019/08/10/Cpp-RVO(Return-Value-Optimization).html
func이 반환하는 객체를 대입하고 있는데.. 반환값은 지역 객체이므로 해제되는 값인데? 반환하고 있으므로 컴파일러는 필수적으로 임시객체를 만들어야 함.
따라서 복사 생성자를 호출하고 있음.
: 이때는 암묵적으로 만듬.
: 반환하는 값타입 함수에서 임시객체를 바로 반환하고 있어서,
위의 빌드업 그림처럼 생성 , 복사가 이루어지지 않고 있음.
: 이름이 있는 객체도 RVO가 가능하게 하는 것.
cl 컴파일러는 설정을 해야 함.
보통 이러한 경우에는 많이 없지만,
위와 같이 외부의 객체가 함수의 인자로 보내는 용도로 사용할 때 사용.
: 전역 객체라고 하더라도, 값으로 리턴할 경우, 어쨋든 임시객체가 만들어짐.
왜냐하면? c++에서 값반환은 임시객체를 만들기 때문임.