C++ 포인터 주소값 저장하기
간혹 포인터의 주소 연산자 (&)를 이용하여 역참조 등을 할 일이 있다.
근데 절대 기억하지 못하는 특정 개체를 해제하지 않고 주소만으로 불러와야할 때도 있다.
어떤 값의 주소를 이용하여 역참조를 해보자.
Foo.h
class Foo {
public:
Foo(const int value);
~Foo();
int getValue();
private:
int value;
};
우리가 잃어버릴 클래스다. value 변수는 생성자에서 설정한 value로 설정된다.
Testsuite.cc
#include <iostream>
#include <cstdint>
#include <sstream>
#include <string>
#include "Foo.h"
int main() {
Foo foo(10);
std::cout << &foo << std::endl;
std::string dataAddress; {
std::ostringstream ostream;
ostream << reinterpret_cast<std::uintptr_t>(&foo);
dataAddress = ostream.str();
}
std::cout << dataAddress << std::endl;
std::uintptr_t p; {
std::istringstream istream(dataAddress);
istream >> p;
}
std::cout << p << std::endl;
Foo *lost = (Foo *) reinterpret_cast(p);
std::cout << lost->getValue() << std::endl;
return 0;
};
굳이 std::string을 시용하여 주소를 저장해야하는지, 그건 필요에 따라 다를 것이다.
JNI와 같이 Java한테는 주소값을 넘겨줄만한 적당한 타입이 존재하지 않는다.
uintptr_t를 구현해서 Java에서 C++과 같이 주소를 저장하고 있다 다시 돌아와서 그 객체를 풀고 맞게 주소를 불러오기 보단
그냥 std::string <-> java.lang.String을 하는 것이 훨씬 나을 것이다.
하다못해 Java에서도 Unsafe 클래스를 이용하여 메모리 주소값으로 생성된 객체를 불러올 수 있는데
Native 언어로 그러지 못할리는 없을 것이다. 조금 불친절할 뿐이지.
추가1) 간혹 std::uintptr_t를 일반적인 primitive type으로 사용하는 방법에 대해서 묻는데, 이건 대상 플랫폼과 운영체제의 선언에 따라 다르다. 예를 들어 MSVC는 64비트에 unsigned __int64, 32비트에 unsigned int로 선언했고 GCC는 64비트에 unsigned long int, 32비트에 unsigned int로 선언되어있다. 필요에 따라 사용해야할 것이다.
추가2) 또한 이 기능 자체가 C++11부터 표준이 된 기능이다. 이전 표준에 대해서는 <cstdint>가 아닌<stdint.h>라는 C헤더를 이용해서 작성해야한다.