복사 생성자를 공부하던 중

이러한 코드에서 복사 생성자가 호출되는가? 에 대한 자문을 해보았을 때

MyClass a = 2; // MyClass(int) 으로 오버로딩된 생성자가 있으니 type casting가능
MyClass a = MyClass(2); //임시객체 생성으로 오버로딩 생성자 호출
MyClass a(MyClass(2)); //복사생성자 호출하여 복사

이러한 과정으로 예측을 하여, 나는 무조건 호출되겠지 라고 생각을 했었다.

2가 임시객체가 되는 이유 : https://ddidding.tistory.com/98

 

근데 실행을 해보면 

복사 생성자가 실행이 되지 않는다.

어셈블리로 확인을 해보아도 오버로딩 생성자 하나만 호출될 뿐이였다.

호출되는 생성자는 복사 생성자가 아닌 오버로딩 생성자

즉, 이러한 과정이 된다는건데

MyClass a = 2 -----> MyClass a(2)

 

아무리 찾아도 저렇게 변환된다는 문법을 찾을 수 가 없었다 ㅅㅂ..😢

 

그러다가 이게 정말 생략되었다는 느낌이 강하게 들어서 컴파일러의 최적화 문제인가 싶어서 검색중에

Copy elision이라는 컴파일 옵션을 알게되었다.

 


Copy elision


Copy elision : 객체의 불필요한 복사를 제거할 수 있는 컴파일러 최적화 기술

std 12/31

이러한 Copy elision는 다음과 같은 상황에서 허용된다.

std 12/31

이 중에 지금 31.3을 해석 해보면 딱 지금과 같은 상황이다.

참조에 바인딩되지 않은 임시 클래스 객체가
동일한 유형의 클래스 객체로 복사/이동될 때(cv-unqualification 일때),
임시 객체를 대상에 직접 생성하여 복사/이동 작업을 생략할 수 있습니다.

MyClass a = 2; 에서

참조에 바인딩 되지 않은 임시 클래스 객체

= 2가 type casting으로 MyClass(2)가 되면서 임시객체가 됨

동일한 유형의 클래스 객체로 복사될 때(const, volatile가 아닐때)

= a와 임시객체는 같은 MyClass 타입으로, 동일한 유형의 클래스 객체로 복사 된다고 볼 수 있다.

 

코드로 보자면 이러하다.

copy elision이 된 경우는 복사가 생략되어 임시 객체가 생성이 되지 않는다.

 

그런데 "임시 객체를 대상에 직접 구성" 이라는건 무슨 뜻일까?

이게 나에겐 두가지 의미로 다가와서 굉장히 헷갈렸었다.

더보기

 

1. 임시 객체를 생성하고 그걸 대상에 직접 구성한다.

MyClass a = MyClass(2)
//copy elision
MyClass a(임시객체)

2. 임시 객체를 생성 하지 않고 대상에 직접 생성자를 호출하여 최적화

MyClass a = MyClass(2)
//copy elision
MyClass a(2)

굉장히 여러군데 찾아보면서 생각했다.

근데 애시당초 1번은

지금 복사 생성자가 호출이 안되서 찾아보고있는데 MyClass a(임시객체)에서는 복사 생성자 호출이 돼서

사실 생각할 것도 없었다. 나는 버드..?

게다가 답이 2번이라는 사실은 어셈블리에 이미 답이 있었다..ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅅㅂ...

 

약간 임시객체에 꽂혀서 "임시 객체를 대상에 직접 구성" 을 보니까

"임시객체가 생성된다고ㅅㅂ?!!?!" 라고 바로 생각도 안하고 머리속에서 정의내려버리니까

답을 찾는데 굉장히 시간이 더 걸렸다.

 

다음부터는 좀 더 자세히 뜻을 파악하고 근본 원인에 대해 신경은 쓰고 있어야겠다.

한글을 이해 못해서 일어난 문제라 씁슬하다...

 

복사/이동 작업을 생략한거지

임시객체의 생성을 생략한건 아니지않나?

라는 생각도 들었었는데

복사가 생략되면서 임시객체의 생성도 생략되었다.

 

자 여기서 복사 대입 연산자는 copy elision이 적용되는가? 한다면

참조에 바인딩되지 않은 임시 클래스 객체

라 명시되어 있기 때문에 더 생각할 필요는 없겠다.

 


 

결론

copy elision라는 최적화 기능으로 인해 복사 생성자가 생략되는 경우가 있었다.

그 방법은 임시객체가 대상으로 바로 생성되게하여 복사를 생략하는 방법이였다.

 

그리고 copy elision에 대해 함수부분으로 RVO, NRVO같은 개념이 있는데 이건 일단 나중에 다뤄야겠다.

 

 

https://shaharmike.com/cpp/rvo/

 

Return Value Optimization | Shahar Mike's Web Spot

Return Value Optimization (1096 words) Fri, Aug 18, 2017 Return Value Optimization (RVO), Named RVO (NRVO) and Copy-Elision are in C++ since C++98. In this post I will explain what these concepts mean and how they help improve runtime performance. I will u

shaharmike.com

 

https://stackoverflow.com/questions/6383639/conditions-for-copy-elision

 

Conditions for copy elision?

I wanted to verify the if the following optimizations work as expected: RVO Named RVO Copy elision when passing an argument by value So I wrote this little program: #include <algorithm> #i...

stackoverflow.com

https://kldp.org/node/158078

 

c++ string 클래스의 원리에 있어 궁금한 것이 있습니다. | KLDP

글쓴이: 익명 사용자 / 작성시간: 토, 2017/08/12 - 10:05오후 연습으로 string 클래스를 만들어보고 있는데요(아주 기본적인 것들로만요..하하하) 일반적으로 string 클래스를 이용할 때 string str1 = "Happy c

kldp.org

https://kldp.org/node/157183

 

c++ 생성자에 반환형 없나요? | KLDP

글쓴이: 익명 사용자 / 작성시간: 수, 2017/03/15 - 6:14오후 제가 생각했을때 컴파일 타임에 컴파일러가 추가시킬거 같은데 그래야 Class c = Class(); 이런 생성자 반환값 대입 문장이 유효하지 않나요?

kldp.org

https://en.wikipedia.org/wiki/Copy_elision

 

Copy elision - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Compiler optimization in the C++ programming language eliminating unnecessary copying of objects In C++ computer programming, copy elision refers to a compiler optimization technique t

en.wikipedia.org

https://stackoverflow.com/questions/1758142/why-copy-constructor-is-not-called-in-this-case#comment1649230_1758617

 

Why copy constructor is not called in this case?

Here is the little code snippet: class A { public: A(int value) : value_(value) { cout <<"Regular constructor" <<endl; } A(const A& other) : value_(other.

stackoverflow.com

https://ko.cppreference.com/w/cpp/language/copy_elision

 

copy elision - cppreference.com

복사 및 이동 생성자를 무복사 값에 의한 전달(zero-copy pass-by-value)로 최적화 하는 것을 말합니다. 아래와 같은 상황일 때 컴파일러는 객체의 복사 및 이동 생성자를 생략합니다. 심지어 복사/이동

ko.cppreference.com

https://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization/12953145#12953145

 

What are copy elision and return value optimization?

What is copy elision? What is (named) return value optimization? What do they imply? In what situations can they occur? What are limitations? If you were referenced to this question, you're proba...

stackoverflow.com

내가 이번에 잘못된 기능으로 파악하고 있었던 클래스의 전방선언

 

1. Include없이 전방선언만 했을 때 사용하지 않아도 오류가 없다.

 

2. 전방선언한 클래스의 크기를 사용한다면 함수의 정의가 없어도 괜찮다.

 

지금 테스트하는 파일 구조를 설명하면

여기서 프로젝트1만 실행된다. 

MyClass에는 클래스 MyClass가 있다.

(좌)헤더파일 / (우)cpp파일

main.h 에서 myInclude.h만 Include하는 상황이다.

고로 main.h는 MyClass.cpp는 아에 모른다.

이렇게 cpp는 몰라도 객체가 생성이 될까?

 

멤버함수는 클래스의 크기에 들어가지 않는다.

객체 생성은 클래스의 크기만 알면 되기때문에

헤더파일의 멤버 변수만으로도 클래스의 크기를 알수있다.

클래스의 크기를 알수있다는 것은 객체 생성은 물론이고 연산까지 가능하다.

나는 바보같게 함수도 꼭 정의되어야 이렇게 객체 생성이나 다룰 수 있을줄알았는데 ..

생각해보니 이게 맞았다.ㅋㅋㅋ

 

물론 이 상태에서 함수를 사용하면 에러가 난다.

'C++ > 헷갈리는것들 정리' 카테고리의 다른 글

복사생성자가 왜 호출이 되지 않나? (copy elision)  (0) 2022.03.28
주소 관련  (0) 2021.07.15
malloc & new & delete [작성중]  (0) 2021.06.29
  • 더블포인터에 이차원 배열의 이름이 대입되지 않는다
더보기

 

배열의 이름은 첫번째 원소의 주소이다

a의 첫번째 원소는 int형의 주소, 그러므로 포인터에 대입이 가능하다

 

aa의 첫번째 원소는 &(aa[0][0])이 아니라 &(aa[0]) 이다

예로 

첫번째 원소는 int형을 5개 가진 aa[0]이고

두번째 원소는 int형을 5개가진 aa[1]이다.

고로 aa는 int[5]의 배열이다

 

그럼 &(aa[0]) 의 자료형은 int (*) [5](int형 5개를 가진 포인터)

int형의 더블포인터 와 int[5]를 가진 포인터는 다르다

 

paa는 int*의 주소이다(int**)

그러므로 int(*)[5]와 int** 는 다른 자료형이기에 대입이 되지않는다 

+1을 하면 더해지는 주소의 양이 다르다

 

이게 헷갈리는 이유중 하나는 둘다 참조를 두번하면 int형이 나오는게 동일해서일듯싶다

밑에서 aa[0][0]과 paa[0][0]의 자료형은 같다

aa[0]과 paa[0]의 자료형은 위에서처럼 다르다

 

배열자료형을 잊지말장

 

 


  • 포인터는 1차원배열과도 같다고 생각한다

계속해서 작성중 입니다.


 

Malloc & new

 


  • malloc은 초기값을 설정할 수 없다, new는 가능하다

 


  • malloc은 생성자가 호출되지 않는다, new는 생성자가 호출된다
더보기

 

 

eh vector constructor iterator에서 생성자 호출?

 

 


  • malloc은 Realloc으로 메모리블럭의 재할당이 가능하다, new도 실행하다?
더보기

구글링을 하면 new는 realloc이 되지 않는다는 글이 많다.

 

물론 사용안하는게 정신건강에 좋겠지만

 

그래도 실행은 되지 않을까? 하는 마음에 테스트 해봤다

 

밑의 코드로 malloc에서 realloc을 하였을 때, new에서 realloc을 하였을 때 를 메모리 디버깅으로 확인해 볼것이다

 

먼저 malloc에서 realloc일때다

 

realloc 실행 전
realloc 실행 후

 

realloc이 다른 주소를 뱉어내는 바람에 원래 들어 있었던 값 9가 다른 주소로 옮겨지는건 확인했지만

우리가 원한 16바이트 만큼 할당이 된건지 알 수 없다

쥰내 안나와서 코드좀 덧붙였다

위의 사진에서 realloc으로 재할당 받는걸 볼 수 있다

 

저 일정하게 채워져 있는 cd와 fd가 무슨 뜻이 있을거 같아서 검색해봤는데 힙에서 넣어주는 값이라고 한다

앞뒤의 fd는 가드 바이트라 하고 cd는 초기화가 되지 않았다는 뜻이라한다

이 글에 자세히 나와있다

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=ruvendix&logNo=220905335341 

 

 

아무튼 이제 new로 확인해보겠다

진짜 같은 주소 안나와서 가챠하는 기분이였다

마찬가지로 원래 있던 값 9가 있으며 앞뒤로 가드바이트가 있으며 빈 영역에 cd로 채워져있다

 

이건 realloc이 실행됬다고 생각을 한다

 

new도 realloc이 되기는 한다

 


  • mallock과 new의 반환형
더보기

malloc은 void * 를 반환한다

 

new는 자료형을 추가로 입력받아 자동으로 캐스팅하여 반환해준다

 

근데 사실 차이까지라고 해야 하나 싶다 그럴거면 그냥 이름도 차이난다고 하지


  • new는 내부적으로 malloc을 사용한다
더보기

 

위에 있는 코드를 어셈블리어로 F11을 눌러서 계속 들어가 확인해 보겠다

 

1. new의 실체인 operator new가 call이 된다

1

 

2. operator new가 있는 곳으로 간다

2

 

3. 들어가보면 malloc이 나와있다

3

 

 

4. 과연 같은 malloc일까? 하여 malloc의 실행 주소가 같은지 확인해본다

4

 

5. 먼저 new의 malloc이다

5

 

 

6. malloc의 malloc이다

6

 

7. 두 함수 모두 0172E340 부터 malloc이 실행된다 이를 통해 같은 malloc이라고 생각을 하고

new의 구현에 있어서 다르겠지만 현재 사용하는 VC++의 new는 내부에서 malloc이 실행된다고 생각한다

 


  • __declspec(allocator) [진행중]
더보기

먼저 New가 실행시에 실행되는 함수 operator new이다

new에 F12를 눌러 확인할 수 있다 // _VCTRT_ALLOCATOR에 F12를 눌러 확인할 수 있다

_VCRT_ALLOCATOR 이 __declspec(allocator) 으로 define되어 있다

 

 

이번엔 malloc의 실 함수이다

여기도 비슷하게 생긴 _CRTALLOCATOR 이란게 있다

여기도 __declspec(allocator)이 나온다!

 

두개에 모두 들어있기도 하고 ALLOCATOR라는 단어가 들어있어서

메모리 할당 요청에 근접한 기능이 있는가 해서 알아보았다

 

 

https://docs.microsoft.com/ko-kr/cpp/cpp/declspec?view=msvc-160 

 

__declspec 이란 대강 뒤에 붙는(ex 위에서 allocator) 특성? 같은걸 저장소 클래스에 지정할 수 있게 해주는 키워드 같다

 

그럼 이 allocator가 특성이라 하니 살펴봤다

 

 

https://docs.microsoft.com/ko-kr/cpp/cpp/allocator?view=msvc-160 

allocator 선언 지정자는 사용자 지정 메모리 할당 함수에 적용 되어 ETW(Windows용 이벤트 추적) (ETW)를 통해 할당을 표시할 수 있습니다.

 

잘 모르겠지만 allocator라는 특성을 설정하고 ETW로 무언가 디버깅?? 같은걸 할수 있다는 말 같은데

ETW 문서도 보았다

 

 

 

https://docs.microsoft.com/ko-kr/windows-hardware/drivers/devtest/event-tracing-for-windows--etw-

https://docs.microsoft.com/ko-kr/visualstudio/profiling/custom-native-etw-heap-events?view=vs-2019 

ETW (Windows 용 이벤트 추적)는 사용자 모드 응용 프로그램 및 커널 모드 드라이버에서 발생하는 이벤트를 추적하고 기록하는 메커니즘을 제공합니다. ETW는 Windows 운영 체제에서 구현되며 개발자에게 빠르고 안정적이며 다양한 이벤트 추적 기능 세트를 제공합니다.
Add the __declspec(allocator)
 decorator to any function in your custom heap manager that returns a pointer to newly allocated heap memory. This decorator allows the tool to correctly identify the type of the memory being returned. For example:

최종적으로 보아하니 __declspec(allocator)는 직접적인 할당의 요청 이런게 아니라 그냥 단순 속성을 정하고

 

디버깅을 할때? 그런 용도로 사용되는거 같다..

 

V가 붙고 안붙고의 차이는 모르겠다..

 


 

delete [ ]

 

 

 

new로 할당 받은 배열 포인터의 해제다

 

delete [ ] 포인터   로 해제를 한다

근데 사실 delete만 사용해도 해제가 된다

[객체 배열 같은 경우에는 소멸자 문제가 있음]

delete 전 // array를 동적 할당 받아옴 각 1, 2, 3 이 들어오는걸 볼수있다
delete 후 // int * 형을 해제 시켰을 뿐인데 모두 해제가 되는 기적을 본다

 

근데 우리가 몇개의 배열을 할당 받은 줄 알고 시작주소만 넘겨주어도 모두 해제가 이루어지는가?? 궁금했다

 

 

일단 사전 지식을 알아야 이해할 수 있었다

 

1. 일단 하나의 프로그램이 시작되면 그 프로그램에게 하나의 힙메모리가 주어진다 이는 Default Heap이라 한다

   (Default Heap, Default process heap 이렇게 부르는 듯 하다)

 

2. Default Heap은 다양한 메모리크기의 요청에 응답하려 여러 메모리의 크기로 나누어져있다

   이 나누어진 메모리들을 메모리 블럭 이라 한다

 

3. Windows에서 이 Default Heap을 관리하는 Heap manager라는 시스템이 있다

 

4. 우리가 힙에 메모리할당을 요청하면 이 HeapManager가 Default Heap에서

   요청한 크기에 알맞는 메모리블럭을 찾으면 그 첫번째 주소를 반환한다

 

5. 이때 Heap manager에 지금 할당해 준 크기를 저장한다

우리가 free나 delete로 시작주소만 넘겨도 모두 해제가 될 수 있었던 이유는
저 Heap manager에 할당해 준 크기가 저장되어 있기 때문이였다
[ 힙매니저에 저장되는 방식이라던지, 어디에있고 어떻게 관리되는 부분은 아직 내게 필요하지 않은 부분같다 ]

 

6. 여기까지 malloc 의 대략적인 할당 과정이다.

 

※ new의 동적할당과 new의 배열동적할당 어셈블리 차이 확인하기

 


 

작성시 많은 도움이 된 글

https://kfgd.tistory.com/18?category=974115 

 

검증이 어렵고 어려운 내용이 많아 틀린부분이 많을거 같습니다.

 

잘못된 부분이 있다면 알려주시면 감사드립니다.

+ Recent posts