Abseil Tip 3 문자열 연결과 operator+ vs. StrCat()

제목: “이번 주의 팁 #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;

이때 foobar의 내용은 임시 객체에 복사된 후 최종 문자열 객체 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()를 사용하세요. 이를 통해 불필요한 복사와 임시 객체 생성을 방지하고 성능을 크게 개선할 수 있습니다.

더 많은 정보는 문자열 가이드에서 확인하세요.