이것을 알아내는데 1일이 넘게 걸렸다.
상황은 다음과 같다.
struct CGEditLightCommonFactor { glm::vec3 lightPosition; glm::vec3 lightDirection; // Light Colors glm::vec3 lightAmbient; glm::vec3 lightDiffuse; glm::vec3 lightSpecular; // Attenuation except Dir Light bool isRangeRender; float AttnConstant; float AttnLinear; float AttnQuadratic; float AttnRadius; bool isShadow; bool isShadowMapRender; bool isShadowFrustumRender; };
먼저 이 구조체가 여러 클래스에서 공용으로 쓰일 것인데
class CGEditLightObject : public CGEditObject // Child2 of Base Class { public: CGEditLightObject(); CGEditLightObject(CGAssetManager& am); private: CGEditDirLight m_dirLight; CGEditPointLight m_pointLight; CGEditSpotLight m_spotLight; /*** Light Common Properties ***/ CGEditLightCommonFactor m_CommonlightFactors; void updateRadius(); /*** Light Common Properties ***/ };
이 클래스에서 DirLight, PointLight, SpotLight가 아래에 있는 m_CommonlightFactors를 공용으로 쓸 것이다. 나는 공용으로 쓰기 위해서 그 클래스들을 이렇게 배치했는데,
class CGEditDirLight { public: // Constructor to init all member variables to prevent // from accessing to the garbage value CGEditDirLight(); void initialize(CGAssetManager& am, CGEditLightCommonFactor* factor); private: CGEditLightCommonFactor* m_lightFactors; };
EditLightObject 클래스의 AssetManager를 받는 Constructor에서 EditDirLight의 initialize 파라미터를 사용하여 LightCommonFactor의 포인터를 받아 lightFactor의 메모리주소를 받게 하였다. Point랑 Spot도.
근데, 나중에 보니까 이 LightCommonFactor의 메모리주소가 다른 것이였다.
상황은 이랬는데
내가 DeferredRenderer 클래스의 멤버변수로서
EditLightObject를 선언해놨으며, AssetManager를 받지 않는 Constructor가 실행되고
그 초기에 메모리 선언이 다 되어져 있기 때문에
0x00100000 의 주소를 갖는다고 가정하자.
아마 그 때의 EditLightObject의 LightCommonFactor의 메모리 주소는
0x00101000 이라고 가정하자.
근데 DeferredRender에서 또 EditLightObject를
다음과 같이 실행했었다.
editLights.push_back(CGEditLightObject(assetManager));
또는
test1 = CGEditLightObject(assetManager);
이렇게 실행했었다.
editLights는 c++ STL vector class이다.
어쨋든 여기에서 내가 알아야할 것은
push_back안의 CGEditLightObject(assetManager) constructor를 호출한다는 것
그리고 test1에 CGEditLightObject(assetManager)를 호출한다는 것이다.
이 때 무슨 일이 일어나는지가 중요한데,
editLights vector가 선언된 memory space가 있다.
push_back하기전에 CGEditLightObject가 어떤 메모리 영역에서 constructor를 실행한다.
근데 이것은 editLights의 메모리영역이 아니기 때문에,
push_back할 때, (정확히 내부구현이 어떻게 된지는 아직 보지 않았다) 그 constructor가 실행한 메모리 영역을 editLights로 memory offset을 시켜버린다.
그러니까 메모리 주소값으로 설정하자면
editLights벡터 주소값이 0x00500000이라면
CGEditLightObject constructor가 실행한 메모리 영역이 0x00200000이라면
editLights에 push_back할 때 그 클래스가 가진 모든 메모리 영역을 editLights 벡터 주소값으로 offset시킨다.
따라서, CGEditLightObject의 constructor가 LightCommonFactor의 주소를 DirLight에 넣어줄때는 0x00200000 대의 메모리 주소라면, push_back에 넣고나서
CGEditLightObject의 LightCommonFactor가 0x00500000 대로 바뀌어버린다.
그런데 DirLight 클래스의 포인터를 받는 메모리값은 그대로 유지되어 버리기 때문에, 그것은 0x0020000에 머물러 버려서 문제가 생기고 있는 것이다.
여튼 클래스 내에서 이렇게 메모리값을 받아서 사용할 때 constructor를 이용해서 무언가를 조작할 때 메모리 주소가 어떻게 쓰이는지 주시해서 써야 한다.
이걸 고치는데 거의 1일 넘게 쓰다니...
이것을 고치는 방법은
constructor를 쓰지 않고 그냥 initialize라는 method를 또 CGEditLightObject에 만들어주어서, 하면 된다.
여튼, 뭐 unique_ptr인가 c++ 뭐시기 기능도 한 번 알아보면 좋을듯 하다.
어쨋든 디버깅 했다. 해결!
================================================
https://stackoverflow.com/questions/24523548/when-we-use-constructors-on-the-heap-and-constructors-on-the-stack
여기 답변을 통해 명확히 답변할 수 있겠다.
new는 Heap으로 부터 할당한다.
근데 단순히 Constructor를 사용하는 것은 Stack으로 부터 한다.
따라서 Stack 메모리 영역주소대가 복사되어져 있던 것이다.
댓글 없음:
댓글 쓰기