[C++] emplace_back 과 push_back의 차이점

도윤·2023년 10월 17일
1

C++

목록 보기
4/4

여느때처럼 c++로 코딩을 하다 한가지 궁금점이 생겨났다.

궁금점의 시작은 젯브레인의 코드 리펙토링 메세지였다.

위 메세지는 vectorpush_back() 함수 대신 emplace_back() 을 쓰는 것이 더 좋다는 내용의 조언이다.

해당 메세지는 vector 를 사용할 때에 자주보던 메세지여서, 아무 생각없이 emplace_back() 으로 수정하여 썻었는데, 문득 emplace_back()push_back() 과 무슨 차이가 있길래 수정하여 쓰라는 것인지 궁금해져 정리해보게 되었다.

emplace_back VS push_back

해당 두 함수는 vector 안에 객체를 넣는 방식에 차이가 있다고 한다.

결론부터 말하자면, push_back() 함수는 만들어진 객체를 집어넣는 형식이지만 emplace_back() 은 함수를 구성하는데에 필요한 정보를 넘겨주고 함수내에서 객체를 생성하여 삽입하는 방식이라고 한다.

말로만 들어선 이해하기 힘드니 예제 코드를 보며 이해해보자.

push_back

int main(){
	vector<Item> m_vecItem;

	std::cout << "excute push_back()" << "\n";
    m_vecItem.push_back(Item(1));
}
[output]

excute push_back()
일반 생성자 호출
이동 생성자 호출
소멸자 호출
소멸자 호출

push_back() 을 통해 Item 객체를 삽입하기 위해

  1. Item(1)을 통해 임시객체를 생성함
  2. 임시객체를 이동생성자를 통해 함수내에 임시객체를 만듬
  3. 벡터에 객체를 삽입함

의 과정을 거치게 된다.

이제 emplace_back() 의 과정도 봐보자.

emplace_back

int main(){
	vector<Item> m_vecItem;

	std::cout << "excute emplace_back()" << "\n";
    m_vecItem.emplace_back(1);
}
[output]

excute emplace_back()
일반 생성자 호출
소멸자 호출

emplace_back() 을 통해 Item 객체를 삽입하기 위해서는

  1. emplace_back함수에서 Item객체를 만들기 위해 필요한 정보(매개변수)를 넘김
  2. 함수내부에서 임시객체가 생성됨
  3. 벡터에 객체를 삽입함

의 과정을 거치게 된다.

이러한 과정을 봐보니 객체를 삽입할 때에 emplace_back() 을 사용하면 불필요한 객체 생성을 하나 줄일 수 있다는 것을 깨닳았다.

결론

emplace_back()push_back() 중 어느것이 더 효율적이다. 라고 말하기는 힘들지만, 두 함수의 차이점을 생각했을 때 다음과 같은 이슈가 발생할 수 있다.

int main(){
	vector<vector<int>> v;
}

다음과 같이 v 가 이중 벡터로 되있는 경우 push_back() 를 사용하면 다음과 같이 데이터를 넣을 수 있다.

vector<int> v1 = { 1, 2 };
v.push_back(std::move(v1));

이 때 다음과 같이 데이터를 넣는 경우는 매칭되는 생성자가 없어 오류가 발생하게 된다.

v.push_back(10); 	// <- ERROR

하지만, emplace_back() 을 사용하면 오류가 발생하지 않게 된다.

v.emplace_back(10);		// <- NOT ERROR

이는 v[0] 안에 10개의 새로운 공간을 할당하라는 뜻이기에 발생하는데, 이처럼 emplace_back() 은 컴파일 타임에서 에러를 잡아내지 못하기에 의도하지 않은 결과가 나올 수 있다.

하지만, emplace_back() 은 동일한 상황에서 이동 생성자를 호출하지 않는다는 장점이 있으므로, 이동 작업이 비쌀 경우에 사용하면 이점을 볼 수 있다.

profile
Game Client Developer

0개의 댓글