현재 구현하고 있는 Wall run은 Character movement mode를 Flying으로 설정하여 사용하고 있고

Wall run중에 땅에 닿으면 자동으로 해제를 하려고 했다.

 

기존에 착지 이벤트를 Landed를 override하여 사용하고 있어서

자동으로 되겠거니 했는데 안타깝게도

Flying모드에서는 Landed가 호출이 되지 않더라 =~=;;

 

Trace를 사용하여 하는 방법이 있겠지만 뭔가 따로 Trace를 쓰지 않는 섹시한 방법이 있지 않을까? 했다..

그래서 CharacterMovement를 보던 중에 뭔가 그럴듯한 함수가 있었다

 

CharacterMovement에 있는 FindFloor함수를 사용한다.

Capsule에 collision이 켜져있지 않으면 의미없다고 한다

 


ACharacter를 상속받은 클래스에서의 사용

첫번째 매개변수는 캡슐의 위치를 넣는다고 하는데 나는 캐릭터에 붙어있는 캡슐의 위치를 원하니까

GetActorLocation과 같은 위치이기 때문에 GetActorLocation를 넣어주었다.

 

두번째는 결과값이 담기는 구조체

 

세번째는 마지막 테스트 값을 사용할수있다 인데  아직은 잘 모르겠다.

 

IsNearlyZero의 0.007f는 Wall run중에 바닥에 닿으면 0.005정도 오차가 있었기에 0.007로 오차허용을 해주었다.

 


Wall run중에 깔끔하게 지면으로 내려간다 =~=

 

문제 : 

C에서 작성한액터 액터 컴포넌트의 디폴트 값을 정하려 했는데 암것도 안나오길래 뭐지 했다.

 


문제 해결

전에 비슷하게 난 아무것도 안했는데 액터 컴포넌트를 참조 못하는 경우가 있어서 혹시하고 해봤는데 맞더라..ㅜ

 

버전 문제인가??ㅠㅠ

 

https://ddidding.tistory.com/52

 


느낀점

시간 아꼈고~~~~

FVector

 

벡터를 회전하고 싶을때 

const FRotator rot(10, 10, 10);
FVector result = rot.RotateVector(FVector(1, 0, 0));

 

어떤벡터를 Normal값을 알고있는 평면에 투영하고 싶을때

FVector::VectorPlaneProject(vec1, vec2);

 

외적의 매개변수 순서는 Forward, Right 으로 넣어서 UpVector가 나오면다고 생각하면 편하다.

FVector::CrossProduct(forwardVec, RightVec);

'UE4 > C++ 정리' 카테고리의 다른 글

생성자(const FObjectInitializer& ObjectInitializer )  (0) 2022.01.29
[1] UPROPERTY  (0) 2021.06.01

문제 : 

이러한 로그가 나와 해결하려했다.

 


문제 해결

 

로그를 통해 정확히 어느 부분에서 발생했는지는 알수 없지만 확실한건

로그에 나와있듯이 밑의 함수를 사용한 블루프린트에서 나왔다는걸 알 수 있다.

아무튼 그래서 의심되는 블루프린트에서 검색을 해봐야한다.

 

저런 수식같은 함수는 이런 식으로 검색 해주면 나온다.

 

확인해보니

저 UpdateVelocityBlend에서 발생하는거였지만

함수문제가 아닌 로직 문제였다.

 

캐릭터의 스피드로 값을 나누는데 스피드가 0이면 저 함수가 실행되지 않았어야 하는데

상태변경에 문제가 있어서 해결하여 더 이상 나오지 않았다=~=

 


느낀점

warning이 안나오니 넘 시원하고~~~

복사 생성자를 공부하던 중

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

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

  • static 멤버 함수

멤버 함수를 static으로 선언이 가능하다.

 

static 멤버 함수는 어떤 특정 객체와도 결합되지 않기 때문에

 

1. 객체에 의해 호출될 필요가 없고, 객체에 의해 호출될 수 없다.

※static멤버변수는 객체에 의해 호출될 수 있다.

그렇기에 this포인터를 가지지 않는다.

 

2. 어떤 객체와도 결합되지 않기에 static 멤버 함수 내에서 사용가능한 멤버는 static 멤버 변수 밖에 없다.

 

3. 접근 지정자에 영향을 받는다.(public만 자유롭게 사용가능)

 

이러한 특징을 지니고 있기에 클래스의 설정 값, 플래그 같은 걸 설정하기에 자주 사용했다.

 


  • 생성자에 new를 사용할 때 주의 사항

생성자에 new를 사용하여 동적할당 받았다면 소멸자에 반드시 delete로 해제 해주자.

 

new와 delete의 사용은 서로 어울려야 한다.

new는 delete와, new [ ]는 delete[ ] 처럼.

 

생성자가 여러개일 경우에는 new면 모두 new, new [ ]면 모두 new [ ] 로 사용해야한다.

이유는 소멸자가 한개이기 때문에 delete도 한번만 사용할 수 있기 때문이다.

 

깊은 복사가 필요한 클래스라면,

깊은 복사에 영향이 가는 static 멤버들도 갱신을 해주자

 


  • 객체 참조형 리턴시 주의

리턴되는 객체가 피호출 함수에 지역적이라면 참조로 리턴하면 안된다.

참조도 내부적으로 포인터로 작동하기 때문에 함수가 끝나면 쓰레기 값을 참조하게 된다.

절대 하지말자~~~

 


  • 객체와 함께 사용하는 위치지정new

객체와 함께 사용하는 위치지정new를 사용하는 경우 주의할 것이 있다.

 

먼저 생성부분이다.

pb와 pc는 buffer에 각 생성되었다.

하지만 생성되는 위치가 겹쳐 pb가 pc에 묻혀서 데이터가 유실될것이다.

그래서 이렇게 바꿔준다.

 

그리고 메모리 해제 부분인데

저 buffer는 delete[ ]로 해제가 가능하다.

그럼 buffer가 해제 될때 그 안에 있던 pb, pc도 메모리 해제가 될까? 한다면

메모리 해제부분에서는 어차피 buffer라는 하나의 메모리로 할당받았으니

buffer라는 메모리만 돌려준다면 메모리상에서는 이상할게 없다.

 

하지만 객체의 소멸자가 호출되는가의 문제가 생긴다.

그래서 buffer가 해제 되기 전에 안에 있던 객체의 소멸자를 모두 호출해준다.

이렇게 실행하면 오류가 난다.

pa는 기본new로 할당받았으니 delete로 해제하여도 문제가 없고

원인은 pb와 pc의 해제 방법이 잘못됐기 때문.

 

이 delete는 new와 연계되지만 위치지정new와는 연계되지 않는다.

그래서 직접 소멸자를 호출해주어야 한다.

 


  • 특별 멤버 함수 (Special member function)

특정 경우에 컴파일러가 자동으로 생성하는 클래스의 멤버 함수이다.

 

기본 생성자

더보기

값을 초기화하고 싶지 않을 때 편리하며 객체들의 배열을 만들 때는 무조건 사용한다.

혹 매개변수가 있는 생성자도 모든 매개변수에 디폴트 값을 제공한다면 기본 생성자가 될 수 있다.

단, 기본 생성자는 하나만 가질 수 있기 때문에 이러한 코드는 모호하여 불가능하다.

복사 생성자

더보기

 

디폴트 복사 생성자는 static멤버를 제외한 멤버들을 각 값으로 복사한다.(얕은 복사)

디폴트 복사 생성자: 사용자가 정의하지 않아서 C++가 자동으로 만든 복사 생성자

 

새로운 객체가 생성되어 같은 클래스의 객체로 초기화될 때마다 호출된다.

초기화될 때: 초기화는 새 개체를 선언하거나, 함수 인수를 값으로 전달하거나, 함수에서 값으로 반환할 때 발생합니다.

임시 객체를 생성할 때마다 복사 생성자를 사용한다.???

어셈블리어로 확인하면

왼쪽의 call MyClass::MyClass(078119Ah)로 가보면 오른쪽의 코드로 진행된다. 즉, 모두 복사 연산자가 호출 된다.

 

MyClass d = MyClass(a); 같은 코드도

MyClass b(a)와 내부 동작이 완전히 같게 복사 생성자로 바뀌어 진행된다.

 

근데 공부하면서 헷갈렸었던 주의할 것이 있는데 

MyClass d = MyClass(a) 부분처럼 대입 연산자(=)가 사용되었을 때

처리를 두 가지 방법으로 생각해 볼 수 있다.

 

1. 위의 어셈블리처럼 복사 생성자를 직접 사용하는 것

ex) "MyClass d = MyClass(a)"  =>  "MyClass d(a)"

 

2. MyClass(a)부분에서 임시 객체를 생성하고 그 임시 객체를 대입 연산자로 d에 대입하는 두 단계로 나누어 처리

ex) MyClass d = 임시 객체

 

어떠한 방법을 따르는지는 컴파일러에게 달렸지만 복사 생성자는 항상 호출되고 대입 연산자가 있을 시에

대입 연산자가 호출될 수도 있다.

복사 할당 연산자(복사 대입 연산자)

더보기
이런 형식이다.

위에서 객체 = 객체처럼 자연스럽게 대입 연산자를 사용했지만

이것 또한 우리가 정의하지 않아도 사용할 수 있었다, 그 이유는 C++에서 자동으로 만들어주었기 때문이다.

 

ms문서에 보면 초기화와 할당이라는 단어를 제일 먼저 설명해준다.

ms문서 : https://docs.microsoft.com/ko-kr/cpp/cpp/copy-constructors-and-copy-assignment-operators-cpp?view=msvc-170

그 이유는 초기화가 일어날 때는 복사 생성자, 할당이 일어날 때는 복사 할당 연산자가 실행되기 때문이다.

초기화: 초기화는 새 개체를 선언하거나, 함수 인수를 값으로 전달하거나, 함수에서 값으로 반환할 때 발생합니다.

할당: 한 개체의 값이 다른 개체에 할당되면(a = b) 첫 번째 개체(b)가 두 번째 개체(a)에 복사됩니다.

 

이렇게 초기화, 할당이라는 조건으로 나뉘어 실행되는 이유는 내가 생각하기에

새로 객체가 생성되지 않는 경우의 복사 상황에는 복사 생성자가 실행되지 않기 때문이라고 생각한다.

그래서 따로 처리가 필요한 것이고 그 처리가 복사 할당 연산자가 된 것이다.

 

초기화가 아닌 할당이 조건이라는 건,

복사받을 객체가 이미 초기화가 되어있고 어떤 값이 들어있다는 뜻이다.

 

이는 즉, 초기화(복사 생성자)와 달리

깊은 복사가 필요할 때 이전에 있던 동적 할당된 메모리를 필수로 해제시켜주어야 한다는 의미도 있다.

 

그리고 또 하나의 차이점으로 호출한 객체에 대한 참조를 리턴한다

(생성자는 리턴이 없음)

좀 더 쉽게 풀면 하나의 객체를 기존의 다른 객체에 복사할 때 사용된다고 볼 수 있다.

 

이 복사 생성자와 복사 대입 연산자 중 하나라도 정의한다면

나머지 하나를 정의해주는 게 좋다.

그래야 모든 복사 상황에 대해 정의가 되니 보다 코드의 의미가 명확해진다.

이동 생성자

이동 할당 연산자(이동 대입 연산자)

주소 연산자

기본 소멸자

 

모두 각각 정의되지 않았을 경우 자동으로 생성해 준다.

이동 관련은 뒤에 정리


  • 얕은 복사, 깊은 복사

new에 의해 초기화되는 포인터들을 멤버로 가지고 있을 경우에

포인터 자체를 복사하는 것이 아니라, 그 포인터가 지시하는 데이터를 복사하는 복사 생성자를 정의해주면

그것을 깊은 복사라 한다.

 

얕은 복사와 깊은 복사라는 이름이 지어지게 된 이유는

얕은 복사는 포인터가 지시하는 데이터를 복사하기 위해 깊게 파고들지 않고, 그 주소만 얕게 복사하기 때문에

지어진 이름이다.

 

문제로 얕은 복사 시에는 하나의 할당된 메모리를 두 개의 객체가 멤버 변수로 접근하기에

하나의 객체가 소멸하여 소멸자를 호출하여 메모리가 해제될 때

다른 하나의 객체는 원하지 않아도 메모리가 해제된 포인터 멤버 변수를 가지게 되어 큰 문제가 될 수 있다.

 

그래서 깊은 복사를 해주는 것

 


  • 복사 생성자 추가

위의 코드에서 String클래스와 string클래스는 생성자에서 내부적으로 동적할당이 일어난다.

그렇다면 Magazine클래스에 대한 복사가 일어날때 복사 생성자나 복사 할당 연산자를 정의해야하는가?

 

한다면 여기서는 아니다.

 

이유는 Magazine이 복사가 일어나면 디볼트 복사 생성자나 디폴트 복사 할당 연산자는

해당 멤버 자료형의 복사 생성자와 복사 할당 연산자를 사용하기 때문이다.

 

그렇기 때문에 String,string이 각각 복사 관련 함수를 깊은 복사로 잘 정의 했다면 정의한 복사 함수들이

실행되는것이다.

 

근데 같은 클래스가 아닌 다른 클래스에 대한 복사면 예외가 있다고 한다.

뒤에서 나온단다

  • static 멤버 변수

멤버 변수는 객체마다 존재하며 각각 다른 값을 가질수 있는 반면에

static 멤버 변수는 하나의 데이터로 같은 클래스의 객체가 공유하는 멤버 변수이다.

 

클래스 내부에 static키워드를 추가하여 멤버 변수를 선언하며

어떠한 스코프 안에 선언된 단순 정적지역변수와 같은 점도 있고 차이도 있다.

 

1. 차이점으로 먼저 클래스 선언부 안에서 초기화 할 수 없다.

 

이유는 static 멤버 변수는 객체의 일부분으로 저장되는 것이 아니라 별도로 저장되기 때문이다.

그렇기 때문에 초기화 방법이 다른것이다.

 

기본 멤버 변수는 객체가 생성될 때 메모리에 올라가며

각 객체마다 다른 값을 가질 수가 있다.

그렇기에 선언부나 생성자에서 계속 초기화가 이루어져도 상관 없지만

 

static멤버 변수는 초기화될 때 메모리에 올라가게 된다.

초기화가 여러번 되면 매우 혼란스러워지기 때문에

오직 한번만 가능하다.

그렇기에 선언부 외부에 한번 초기화가 가능한것이다.

왼) 초기화부분을 주석 처리 했을 때 메모리에서 찾을 수 없다 // 오) 주석 해제 하면 메모리에 올라가 있는 걸 확인할 수 있다.

 

단 예외로 const를 붙이면 선언부에서 초기화 가능하다.

값이 변하지 않기 때문에 상관없다는 뜻인가?

 

2. 차이점으로 두가지 접근방법이 있다.

 

객체를 통한 방법, 범위 결정 연산자를 통한 접근

 

4. 같은점으로 접근 지정자에 영향을 받는다.

 

public일때만 외부에서 자유롭게 사용할 수 있다.

protected나 private이면 멤버함수, 정적멤버함수, 프렌드함수 로 접근할 수 있다.

 

 

추가로 이 초기화는 헤더파일이 아닌 cpp파일에 넣는다.

프로그램은 헤더 파일을 여러 파일에 포함시킬수 있기 때문에 초기화 구문이 여러 벌 만들어 지는 셈이 되므로

에러가 발생하기 때문이다.


  • 추가

기본 멤버변수가 생성자가 아닌 선언과 동시에 초기화도 가능한 이유? (in-class 초기화)

더보기

예전 C++에서는 이러한 문법이 허용되지 않았다.

그래서 오직 생성자에서만 초기화가 가능했다.

 

하지만 멤버변수 한두 개를 초기화하려고 생성자를 쓰는 게 귀찮았는지

저런 문법이 가능토록 변경되었다고 한다.

 

그 문법을 in class 초기화라 한다

in class 초기화는 

 

in class초기화를 하게되면 멤버이니셜라이저를 사용하는 것과 동일하다.

 

 

 

TODO::차이점 서술하기 

1.하지만 멤버이니셜라이저를 사용하는 모든 생성자는 그에 상응하는 in-class 초기화를 무효화한다.

2.밑에 링크 이해 하기

 

https://blog.naver.com/tipsware/221792822911

 

[Q&A] 멤버 변수의 초기화 방법

: C++ 언어 관련 전체 목차 - http://blog.naver.com/tipsware/221028559903 : C++ 언어 관련 Q&A ...

blog.naver.com

https://stackoverflow.com/questions/27352021/c11-member-initializer-list-vs-in-class-initializer

 

C++11 member initializer list vs in-class initializer?

What difference between these ways of initializing object member variables in C++11 ? Is there another way ? which way is better (performance) ?: class any { public: obj s = obj("value"); ...

stackoverflow.com

 

+ Recent posts