emplace_back() 사용하세요
c++ 벡터를 사용해 왔던 개발자 지망생들이라면 push_back() 을 많이 사용해 왔을 것이다.
결론부터 말하자면 그냥 모든 경우에서 emplace_back()을 사용하는 것이 좋다.
이름과 나이를 가지고 있는 고양이 클래스를 정의하고,
고양이 array를 가지고 있는 cats 를 만들어 보자.
#include <iostream>
#include <vector>
class Cat
{
public:
Cat(std::string name, int age) :mName{ std::move(name) }, mAge{ age }{};
void speak() const
{
std::cout << "meow" << mName << mAge << std::endl;
}
private:
std::string mName;
int mAge;
};
int main()
{
std::vector<Cat> cats;
cats.emplace_back(Cat{ "cat0",0 });
cats.emplace_back(Cat{ "cat1",1 });
cats.emplace_back(Cat{ "kitty",2 });
for (const auto& cat : cats)
{
cat.speak();
}
return 0;
}
emplace_back()을 통해서 "cat0" 0살, "cat1" 1살 이렇게 만들어 주었다.
그리고 for loop문을 통해 고양이 array를 만들어 주었다.
출력하면 위와 같이 나오게 된다.
이후 cats.emplace_back(Cat{"kitty", 2}); 를 통해 "kitty" 2살을 만들어 줄 수 있다.
이를 그림으로 나타내면
cats 벡터는 스택 위에 올라가 있고
힙 공간 안 고양이 오브젝트에 0살, 1살정보가, string 오브젝트에 cat0, cat1 정보가 놓여있다.
벡터 cats는 이 힙 공간을 가리키고
고양이 오브젝트 하나하나는 0살과 1살이라는 정보를 가지고 있으며
이 오브젝트 하나하나는 힙 위에 스트링 오브젝트 cat0, cat1 를 가리키고 있을 것이다.
(실제 컴파일러에서는 string 최적화를 하기 때문에 조금 다를 수 있지만 간단한 string 모델 상에서는 이렇게 될 것이다.)
마지막으로 2살짜리 "kitty"를 cats 배열 안에 넣어주었다.
일반적으로 생각하면 간단하게 0 과 1 뒤에 2 정보를 갖고 있는 고양이 오브젝트가 들어오고
또다른 string 공간이 힙 위에 할당되며 kitty가 만들어지고 이 string 공간을 오브젝트가 가리킬 것이라고 생각하게 된다.
아니다. emplace_back()이 이런 방식으로 들어오게 되면 중간에 한 번의 과정을 더 거치게 된다.
1)스택 위에 임시 고양이 tmpcat(가칭)가 만들어 지고 이 고양이는 2살의 정보를 갖고 있고,
2) 이후 kitty라는 string object를 힙 위에 만든 후에 이곳을 가리키게 된다.
3) 그 다음에 move 라는 operation을 통해서 임시 고양이 오브젝트가 벡터의 마지막 공간으로 move가 되는 것이다.
4)그렇게 되면 이 벡터의 마지막 공간에 kitty 오브젝트를 가리키게 되고 임시로 만든 고양이 오브젝트 tmpcat은 사라지게 되는 것이다.
이런 중간 작업을 없애주기 위한 방법은,
emplace_back() 을 사용할 때 cats.emplace_back(Cat{"kitty", 2}) 이런 식으로 중간에 오브젝트를 만들지 않고
cats.emplace_back("kitty",2) 이런 식으로 생성하기 위한 변수만 넘겨주면 된다!
#include <iostream>
#include <vector>
class Cat
{
public:
Cat(std::string name, int age) :mName{ std::move(name) }, mAge{ age }{};
void speak() const
{
std::cout << "meow" << mName << mAge << std::endl;
}
private:
std::string mName;
int mAge;
};
int main()
{
std::vector<Cat> cats;
cats.emplace_back( "cat0",0 );
cats.emplace_back( "cat1",1 );
cats.emplace_back("kitty",2 );
for (const auto& cat : cats)
{
cat.speak();
}
return 0;
}
이게 어떻게 만들어진 오브젝트를 가리킬 수 있는지 의문이 생길 수 있는데, cppreference 사이트에서 emplace_back 내용을 찾아보면, reference emplace_back(Arg&&... args); 이렇게 되어있다.
즉 C++17 부터는 emplace_back() 이 된 오브젝트의 레퍼런스를 리턴해 주기 때문에
위 코드에서 Cat& cat = cats.emplace_back("kitty",2); 이렇게 cat의 레퍼런스를 받게 해 주면 마지막 공간에 고양이를 가리킬 수 있게 되는 것이다.
(g랭 등 사용자들은 -std=c++17, 이렇게 c++17을 옵션으로 넣어주면 아무 문제없이 컴파일 되는 것 확인할 수 있다.)
'모던C++ > 벡터, 배열 Vector, Array' 카테고리의 다른 글
4. 벡터 루프문 vector array for loop_C++ (0) | 2022.11.20 |
---|---|
3. 벡터 메모리 (reserve, size, capacity, noexcept)_ C++ (0) | 2022.11.08 |
2. 벡터 vector 의 기본 (Time Complexity 시간 복잡도)_C++ (0) | 2022.11.06 |
1. std::vector, 벡터 소개 (1) | 2022.11.06 |