일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- resource management class
- 게임
- lua
- virtual function
- 비교 함수 객체
- 영화 리뷰
- 예외
- exception
- more effective c++
- 암시적 변환
- operator new
- 메타테이블
- 반복자
- c++
- 다형성
- 루아
- Effective c++
- reference
- effective stl
- 영화
- 티스토리챌린지
- 참조자
- Vector
- 스마트 포인터
- 언리얼
- 오블완
- UE4
- implicit conversion
- Smart Pointer
- 상속
Archives
- Today
- Total
스토리텔링 개발자
[Effective STL] 16. C API로 컨테이너 전달 본문
728x90
항목 16. 기존의 C API에 vector와 string을 넘기는 방법을 알아두자
C API
- 배열과 char*를 사용하는 기존 C API들은 여전히 통용되고 있다.
- 이들의 사용을 아예 배제할 순 없기 때문에 넘기는 방법은 알아두는 것이 좋다.
배열 형태로 변환 방법
vector<int> v;
&v[0]; // 배열 형태로 변환
string s;
s.c_str(); // char* 형태로 변환
배열(포인터)로 바꿀 시 주의점
- 빈 벡터를 배열로 변경하려 할 시 주의해야 한다.
-
void doSomething(const int* pInts, size_t numInts); vector<int> v; ... doSomething(&v[0], v.size()); // 헌데, 만약 v가 빈 벡터라면? // &v[0]은 있지도 않은 메모리의 주소값을 가져오려 한다.(미정의 동작) // 그러므로 아래 코드처럼 예외 처리를 해주어야 한다. if(!v.empty()) { doSomething(&v[0], v.size()); }
-
- begin()로 포인터를 가져오려 하지 말자.
- vector의 begin()이 반환하는 반복자는 포인터 일수도, 아닐 수도 있다.(항목 50 참조)
- begin()의 반환 타입은 반복자이지, 포인터가 아니므로 이로 포인터를 대체하려 하지 말자.
-
// 어쩔 수 없이 써야 한다면 아래처럼 사용하자. // &v[0]과 똑같은 포인터를 만드는 표현식이다. &*v.begin()
- vector의 포인터를 얻는 방법은, string에는 통하지 않는다.
- string의 데이터 자체가 연속 메모리에 저장되도록 규정되어 있지 않다.
- string의 내부 문자열 값이 널 문자로 끝나지 않는다.
- 그렇기에 c_str 멤버 함수가 있는 것이다.
- string의 경우 빈 문자열에도 문제 없이 동작한다.
-
void doSomething(const char* pString); string s; ... doSomething(s.c_str()); // 빈 문자열인지 체크할 필요가 없다. // 빈 문자열일 경우 c_str()은 널 문자 포인터를 반환한다.
-
- vector나 string가 전달한 포인터의 값을 변경해서는 안된다.
-
void doSomething(const int* pInts, size_t numInts); void doSomething(const char* pString); // 매개변수 pInts와 pString은 모두 const이다.
- string의 경우 절대로 값을 변경하면 안된다.
- c_str이 문자열 데이터의 내부 포인터를 반환한다는 보장이 없기 때문이다.
- vector의 경우 약간의 융통성이 있다.
- 요소를 변경하는 것은 대개 무난하게 넘어가지만..
- 벡터의 요소 개수(크기)를 바꾸려고 하면 절대로 안된다.
- 정확한 크기를 파악할 수 없게 되면서, v의 내부 데이터가 일관성을 잃게 된다.
- 혹여 크기와 용량이 같은 벡터(항목 14 참조)에 요소를 추가한다거나 하면....
- 어떤 vector의 경우 데이터에 대해 특수한 제약을 가지고 있을 수도 있다.
- 그 특수 제약을, C API 내에서도 지켜줘야 한다.
-
vector 객체를 C API를 통해 초기화하기
// 최대 arraySize 크기의 double 배열에 대한 포인터를 받아,
// 그 배열에 특정 데이터를 기록한다.
// 기록한 특정 데이터 갯수를 리턴하며, 그 크기는 arraySize를 넘기지 않는다.
size_t fillArray(double* pArray, size_t arraySize);
vector<double> vd(maxNumDoubles);
vd.resize(fillArray(&vd[0], vd.size()));
- 이는 vector에만 통한다.
- C/C++ 배열과 동일한 메모리 배열 구조를 가진 컨테이너는 vector가 유일하기 때문이다.
- 그렇지만, string도 방법이 없는 것은 아니다.
// 위 함수와 같지만, 특정 char를 삽입하는 함수
size_t fillString(char* pArray, size_t arraySize);
// char 벡터를 생성하여 C API를 사용한다.
vector<char> vc(maxNumChars);
size_t charsWritten = fillString(&vc[0], vc.size());
// vector를 string으로 변환한다.
string s(vc.begin(), vc.begin() + charsWritten);
- 이 아이디어는 다른 컨테이너에도 모두 통용 가능하다.
size_t fillArray(double* pArray, size_t arraySize);
vector<double> vd(maxNumDoubles);
vd.resize(fillArray(&vd[0], vd.size()));
deque<double> d(vd.begin(), vd.end()); // 덱
list<double> l(vd.begin(), vd.end()); // 리스트
set<double> s(vd.begin(), vd.end()); // 셋
- 이 아이디어를 반대로 적용하여, 다른 STL 컨테이너 데이터를 C API에 넘길수도 있을 것이다.
void doSomething(const int* pInts, size_t numInts);
set<int> intSet;
...
vector<int> v(intSet.begin(), intSet.end()); // 벡터로 변경 후
if(!v.enpty())
{
doSomething(&v[0], v.size()); // 포인터 전달
}
728x90
'개발 > Effective STL' 카테고리의 다른 글
[Effective STL] 18. vector<bool> 주의 (0) | 2024.11.27 |
---|---|
[Effective STL] 17. swap 트릭으로 vector 용량 줄이기 (0) | 2024.11.26 |
[Effective STL] 15. string 구현 방식 (0) | 2024.11.22 |
[Effective STL] 14. vector reserve (0) | 2024.11.21 |
[Effective STL] 13. 배열보다 vector (0) | 2024.11.20 |
Comments