Emplace 메서드 정의
- 컨테이너에 키가있는 요소가 없는 경우 지정된 인수로 구성된 컨테이너에 새 요소를 삽입한다.
- emplace를 주의해서 사용하면 불필요한 복사 또는 이동 작업을 피하면서 새로운 요소를 구성할 수 있다.
- 반복자 및 참조자가 무효화되지 않는다.
여기서 말하는 첫 번째, "컨테이너에 키가 있는 요소가 없는 경우"는 map과 같이 중복되는 원소(키 값)를 허용하지 않는 컨테이너에 한정되는 듯하다. 추가로 emplace는 영어로 "배치하다"라는 뜻으로 새로 삽입한다고 생각하면 편하다.
두 번째, "불필요한 복사 또는 이동 작업을 피하면서 새로운 요소를 구성할 수 있다"
이 부분이 가장 중요한데, 보통 2개 이상의 요소들을 묶어서 queue나 vector에 옮기기 전에
pair나 tuple을 만들고, 요소들을 넣고, push하는 과정이 필요하다.(늘어놓고 보니 복잡한 거 같다.) 물론 그 과정도 간단하게 코드로 구현 가능하지만,
emplace를 사용하면 사용자가 요소만 넣어줘도 사용자가 원하는 컨테이너를 찾고 넣어준다.
세 번째, "반복자 및 참조자가 무효화되지 않는다"
c++를 처음 공부할 때 책에서 나온 것 같은 냄새가 나는 문장이다.
이 문장을 레퍼런스에서 괜히 써놨을 리는 없고.. 해서 emplace와 비슷한 vector의 push_back 함수를 찾아보니 해답이 나왔다.
예를 들어 원소가 여러 개 담긴 vector가 있다고 가정해보자.
우선, iterator로 vector의 시작 원소를 가리키게 하고
이후 여러 원소들을 다시 push_back해준 후
반복자(iter)를 통해 vector의 모든 원소들을 출력하게 하면 모두 출력되지 않는다.
왜냐하면 vector의 push_back은 단지 원소를 넣을 뿐, 용량을 늘리지 않는 삽입 작업이고, 그렇기 때문에 그 위치를 지나가는 요소를 가리키는 반복자 및 포인터 (iterator)를 무효화하기 때문이다.
하지만, emplace 함수는 생성에 필요한 인자를 받아 내부에서 생성 및 삽입이 이루어지기 때문에 반복자 및 참조자가 무효화되지 않는다.
C++ MFC에서 Emplace() 메서드 사용법
STL컨테이너 (std::vector, std::map, std::set) 사용할 때, emplace를 활용하면 불필요한 복사 없이 객체를 직접 생성하여 삽입할수 있어 효율적이다.
1. std:: vector에서 emplace_back 사용하기
MFC의 CArray는 push_back이 없고 Add 메서드를 사용하지만, 성능 최적화를 위해 std::vector의 emplace_back를 사용하는 것이 좋다.
✅ 기존 방식 (push_back)
#include <vector>
#include <afxwin.h> // MFC 헤더 포함
#include <iostream>
class CMyClass {
public:
int m_nData;
CMyClass(int n) : m_nData(n) {
std::cout << "CMyClass 생성자 호출됨: " << m_nData << std::endl;
}
};
void UseVectorWithPushBack() {
std::vector<CMyClass> vec;
// 기존 방식: 객체를 생성한 후 벡터에 추가 (불필요한 복사 발생)
CMyClass obj(10);
vec.push_back(obj); // 복사 발생
vec.push_back(CMyClass(20)); // 이동 생성자 호출
}
📌 단점: push_back(obj)의 경우 객체를 한 번 생성한 후 복사가 발생함.
✅ emplace_back으로 최적화
void UseVectorWithEmplaceBack() {
std::vector<CMyClass> vec;
// emplace_back을 사용하면 객체를 직접 생성하여 삽입 (복사 없음)
vec.emplace_back(10);
vec.emplace_back(20);
}
📌 장점:
- 객체를 직접 생성하면서 컨테이너에 추가 → 복사 비용 없음
- 불필요한 임시 객체 생성 없이 컨테이너 내부에서 생성됨
📌 출력 결과 (더 효율적인 방식)
CMyClass 생성자 호출됨: 10
CMyClass 생성자 호출됨: 20
2. std::map에서 emplace사용하기 (MFC CMap 대체)
MFC의 CMap은 std::map의 대체품이지만, 현대적인 C++에서는 std::map이 더 효율적입니다.
✅ 기존 방식 (insert)
#include <map>
#include <string>
void UseMapWithInsert() {
std::map<int, CString> myMap;
// 기존 방식 (std::pair을 생성 후 삽입)
myMap.insert(std::pair<int, CString>(1, _T("MFC")));
myMap.insert(std::make_pair(2, _T("C++")));
}
📌 단점:
- std::pair을 먼저 생성 후 복사하여 삽입 → 불필요한 복사 발생
- 성능 저하 가능성 있음
✅ emplace로 최적화
void UseMapWithEmplace() {
std::map<int, CString> myMap;
// emplace를 사용하면 객체를 직접 생성하여 삽입
myMap.emplace(1, _T("MFC"));
myMap.emplace(2, _T("C++"));
}
📌 장점:
- std::pair을 따로 생성하지 않아도 됨
- 불필요한 복사 없이 객체를 직접 생성하여 추가됨
3. std::set에서 emplace 사용하기 (MFC CList 대체)
MFC의 CList는 STL의 std::set이나 std::vector로 대체할 수 있습니다.
✅ 기존 방식 (insert)
#include <set>
void UseSetWithInsert() {
std::set<int> mySet;
// 기존 방식 (insert 사용)
mySet.insert(10);
mySet.insert(20);
}
✅ emplace로 최적화
void UseSetWithEmplace() {
std::set<int> mySet;
// emplace 사용 (더 최적화됨)
mySet.emplace(10);
mySet.emplace(20);
}
📌 장점:
- insert와 달리 객체를 직접 생성하여 삽입 → 불필요한 복사 없음
- 성능 향상 가능
4. std::list 에서 emplace_back 사용하기
MFC의 CList는 std::list로 대체할 수 있습니다.
✅ 기존 방식 (push_back)
#include <list>
void UseListWithPushBack() {
std::list<CString> myList;
// 기존 방식
myList.push_back(_T("Hello"));
myList.push_back(_T("World"));
}
✅ emplace_back으로 최적화
void UseListWithEmplaceBack() {
std::list<CString> myList;
// emplace_back 사용
myList.emplace_back(_T("Hello"));
myList.emplace_back(_T("World"));
}
📌 장점:
- push_back과 다르게 객체를 직접 생성하여 리스트에 추가됨
MFC에서 emplace를 사용하는 이유
- 불필요한 복사 제거
- push_back(obj) → 복사 발생
- emplace_back(args...) → 복사 없음
- 객체를 직접 생성하여 추가 (생성자 호출 비용 최소화)
- 더 적은 코드로 가독성이 향상됨
- MFC의 CArray, CMap, CList보다 현대적인 std::vector, std::map, std::set 등이 더 효율적
'LAB > C++' 카테고리의 다른 글
MFC 기초 (0) | 2024.12.26 |
---|---|
C++ 기초플러스(15.3 예외처리) / C++로 Mariadb 연결 및 DB Table생성 (1) | 2024.09.28 |
C++ 기초플러스 (13. 클래스의 상속) (1) | 2024.09.27 |
C++ 기초플러스 (12. 클래스와 동적메모리 대입) (0) | 2024.09.26 |
C++ 기초플러스 (08. 함수의 활용 - 10. 객체와 클래스) (0) | 2024.09.25 |