제목: “이번 주의 팁 #3: 문자열 연결과 operator+
vs. StrCat()
”
원문 게시일: 2012년 5월 11일
업데이트: 2022년 11월 16일
빠른 링크: abseil.io/tips/3
개요
리뷰어가 “문자열 연결 연산자 +
를 사용하지 마세요. 비효율적입니다.”라고 말하면 많은 개발자가 의아해합니다. string::operator+
가 왜 비효율적일까요? 이런 간단한 작업에서 실수를 하기 어려울 것 같은데 말이죠.
문제의 원인
비효율성은 분명하지 않은 경우가 많습니다. 예를 들어, 다음 두 코드 스니펫은 실행 시간이 거의 동일합니다:
std::string foo = LongString1();
std::string bar = LongString2();
std::string foobar = foo + bar;
std::string foo = LongString1();
std::string bar = LongString2();
std::string foobar = absl::StrCat(foo, bar);
그러나 세 개의 문자열을 연결할 경우에는 이야기가 다릅니다:
std::string foo = LongString1();
std::string bar = LongString2();
std::string baz = LongString3();
std::string foobarbaz = foo + bar + baz;
std::string foo = LongString1();
std::string bar = LongString2();
std::string baz = LongString3();
std::string foobarbaz = absl::StrCat(foo, bar, baz);
연산자 오버로딩의 한계
C++에는 세 개의 인자를 한 번에 처리하는 operator+
오버로드가 없습니다. 따라서 foo + bar + baz
는 두 번의 string::operator+
호출과 하나의 임시 문자열 객체를 생성하게 됩니다. 이를 풀어서 쓰면 다음과 같습니다:
std::string temp = foo + bar;
std::string foobarbaz = std::move(temp) + baz;
이때 foo
와 bar
의 내용은 임시 객체에 복사된 후 최종 문자열 객체 foobarbaz
에 들어갑니다.
C++11에서는 std::move
덕분에 두 번째 연결이 새로운 문자열 객체를 생성하지 않고 temp.append(baz)
를 호출하는 형태로 최적화됩니다. 하지만 처음 할당된 버퍼의 크기가 충분하지 않으면 재할당과 추가 복사가 필요할 수 있습니다. 최악의 경우 n
개의 문자열을 연결하면 O(n)번의 재할당이 발생합니다.
해결책: absl::StrCat()
와 absl::StrAppend()
absl::StrCat()
absl::StrCat()
은 입력 문자열의 길이를 미리 계산하고, 필요한 크기의 버퍼를 할당한 다음 모든 데이터를 한 번에 연결합니다. 따라서 O(n) 시간 복잡도를 보장하며 매우 최적화된 방식입니다:
std::string foo = absl::StrCat("The year is ", year);
absl::StrAppend()
absl::StrAppend()
는 기존 문자열에 데이터를 추가하는 경우에 유용합니다:
absl::StrAppend(&foobar, foo, bar, baz);
foobar += foo + bar + baz;
와 비교했을 때 absl::StrAppend()
는 불필요한 임시 객체 생성을 피하고 더 효율적으로 문자열을 추가합니다.
다양한 타입 지원
absl::StrCat()
와 absl::StrAppend()
는 문자열 타입 외에도 다양한 기본 타입을 지원합니다:
- 정수형:
int32_t
,uint32_t
,int64_t
,uint64_t
- 부동소수점:
float
,double
- 문자열:
const char*
,string_view
,std::string
예를 들어:
int year = 2023;
std::string message = absl::StrCat("The year is ", year);
결론
문자열을 여러 번 연결하는 경우 operator+
대신 absl::StrCat()
를 사용하고, 기존 문자열에 데이터를 추가할 때는 absl::StrAppend()
를 사용하세요. 이를 통해 불필요한 복사와 임시 객체 생성을 방지하고 성능을 크게 개선할 수 있습니다.
더 많은 정보는 문자열 가이드에서 확인하세요.