IT/C++
[C++] Modern C++ 11/14 핵심 내용 정리
by 웃는펭귄
2025. 8. 25.
class TestClass
{
};
struct NPC {
std::string name;
};
int main() {
std::cout << "Hello, C++!" << std::endl; // 출력
// [0] nullptr
// C++ 03 까지는 널 포인터를 나타내기 위해 NULL, 0을 사용
// 그러나 저렇게 함수에 인자로 넘기는 경우 int 타입으로 추론되어 버리는 문제가 발생
// 모던 C++에서는 널포인터의 의도를 명확히 하기위해 추가됨
// [0] override final
// override - 부모 클래스의 멤버 함수를 재 정의 함
// final - 부모 클래스의 특정 멤버 함수를 자식 클래스에서 재정의 하지 못하도록 막는다.
char* p = nullptr;
// [1] auto
auto NPCName = "NPC_kile";
std::cout << NPCName << " (type: " << typeid(NPCName).name() << ")" << std::endl;
auto count = 1;
std::cout << count << " (type: " << typeid(count).name() << ")" << std::endl;
auto classTest = new TestClass();
std::cout << classTest << " (type: " << typeid(classTest).name() << ")" << std::endl;
std::vector<NPC> npcs = { {"Alice"}, {"Bob"}, {"Charlie"} };
// [2] Range based for
// 복사해도 되는 작은 타입 → auto
// 원본 수정 필요 → auto&
// 읽기 전용 + 복사 방지 → const auto &
// 1. auto → 값 복사, 변경 가능
for (auto npc : npcs) {
npc.name = "X"; // 원본에는 영향 없음 (복사본 수정)
}
// 2. auto& → 참조, 변경 가능
for (auto& npc : npcs) {
npc.name = "Y"; // 원본 수정됨
}
// 3. const auto& → 참조, 변경 불가
for (const auto& npc : npcs) {
// npc.name = "Z"; // ❌ 컴파일 에러 (읽기 전용)
std::cout << npc.name << std::endl;
}
// [4] Enum
// 'unscoped enumeration'과 'scoped enumeration'
// C++98/03의 전통 enum이 unscoped enumeration, C++11에서 새로 생긴 enum class가 scoped enumeration
// 비교 정리
// 특징 Unscoped enum Scoped enum class
// 스코프 없음(전역에 노출) 있음(EnumName::Value)
// 기본 타입 보통 int(컴파일러 결정) 지정 가능(: uint8_t 등)
// int 변환 암시적 허용 명시적 캐스팅 필요
// 값 충돌 가능 불가능
// 타입 안전성 낮음 높음
enum class Color {
Red,
Green,
Blue
};
Color c = Color::Red; // 반드시 스코프 지정 필요
int x = static_cast<int>(Color::Red); // 명시적 변환 필요
// [5] Lambda
// "익명 함수" 필요한 곳에 직접 함수를 정의하고 사용할 수 있게 해줍니다.
// 장점으로는 코드가 간결해지며 가독성이 좋아집니다.
// lambda는 함수 오브젝트힙니다.
// ! 값에 의한 캡처 / 참조에 의한 캡처
// 값 캡처는 변수의 값을 람다에 복사하는 반면, 참조 캡처는 변수에 대한 참조를 람다에 저장합니다.
// 참조를 활용할시 변수가 범위를 벗어나지 않도록 주의해야합니다.
// 함수와 메서드에서도 사용이 가능합니다.
// [캡처리스트] // lambda capture
// (파라미터) // 함수의 인수 정의
// {함수 본문} // 함수의 본체
// (반환 타입) // 함수 호출;
// 캡처리스트? - 람다 표현식이 자신을 둘러싸는 코드의 범위에서 변수를 캡처하 수 있게 해주는 도우입니다.
// * 아무것도 없다면 외부변수를 캡처하지 않습니다.
[] {std::cout << "Hello, Lambda" << std::endl; }();
auto func = [] {std::cout << "Hello, Lambda 1" << std::endl; };
func();
auto func1 = [](int n) {std::cout << "Hello, Lambda 2 : " << n << std::endl; };
func1(2);
func1(3);
// capture lambda를 정의한 scope 내의 변수를 capture 할 수 있다.
// 모든 변수를 참조로 캡처 할 때는 [&], 특정 변수만 참조로 캡처 할 때는 [&변수]
// 모든 변수를 복사로 캡처 할 때는 [=], 특정 변수만 복사로 캡처 할 때는 [=변수]
// generic한 형 표현
auto Sum = [](auto a, decltype(a) b) { return a + b; };
// [6] 스마트 포인터
// 스스로 메모리를 관리하는 포인터로 메모리 누수방지및 복잡성을 줄여준다.
std::unique_ptr<int> smart_ptr1;
// 작동원리 - 객체의 수명이 그 객체가 소유한 자원의 수명과 동일하게 관리되는것
// 즉, 객체가 생성될 때 자원을 할당, 소멸시 자원을 해제합니다.
// 스마트 포인터는 내부적으로 원시 포인터를 본관하고 있습니다.
// 이 원시 포인터는 스마트 포인터가 가리키는 실제 메모리는 가리키고 있습니다.
// 그러나 사용자는 이 원시 포인터에 직접 접근할 수 없으며, 스마트 포인터가 제공하는
// 인터페이스르르 통해서만 메모리에 접근이 가능합니다.
std::unique_ptr<int> smart_ptr2(new int(10));
*smart_ptr2 = 20;
// 스마트 포인터는 소유권의 개념을 도입하여 동일한 메모리에 대한 접근을 관리하므로 덕분에 메모리 해제를 보다 안전하게 만들어줍니다.
// 스마트 포인터 종류
// 1. unique_ptr - 특정 객체를 하나의 스마트 포인터만이 가리킬 수 있게 합니다.
// 소멸시 포인터가 가리키고 있던 객체 또한 소멸됩니다.
// 객체는 하나의 유니크 포인터에 의해서만 가리키고 복사 생성사는 존재하지 않습니다.
// 하지만 객체의 고유권의 이전은 move함수를 통해 가능합니다.
// 1_1 특징
// c++11 스마트 포인터 시작으로 단일소유가 필요한 경우 사용하며 비용이 거의 없습니다.
// 2. weak_ptr - 어떤 객체를 참조하는 포인터가 총 몇개인지를 참조하고 있습니다.
// 이렇게 참조하고 있는 스마트 포인터 개수를 참조 횟수라고 합니다.
// 이 참조 횟수가 0이 되면 더이상 이 객체를 참조하고 있는 포인터가 없다는 뜻이므로 메모리를 자동으로 해제합니다.
// 참조 횟수는 use_count()로 확인이 가능합니다.
// 2_1 특징
// 참조 카운팅 방식으로 동정 메모리를 관리합니다.
// 3. weak_ptr - 스마트 포인터가 순환 참조와 같은 문제에 대처하는데 큰 도움을 줍니다.
// 기본적으로 유사하지만, 가리키는 객체의 수명에 영향을 주지 않는 "약한 참조"를 제공한다는 점에서 차이가 있습니다.
// 순환 참조란? 두 객체가 서로를 참조하고 shared_ptr을 사용해서 참조를 유지하는 경우에 발생
// 3_1 특징
// share_ptr과 같이 참조 카운팅을 하지만 영향이 없습니다.
// 순환 참조 방식 문제를 방지하기 위해 사용 됩니다.
return 0;
}