auto 키워드C++에서 auto 키워드는 C++11에 도입된 강력한 기능으로, 추론된 타입으로 변수를 선언할 때 사용해요.
컴파일러는 초기화 값을 바탕으로 변수의 타입을 추론하는데, 덕분에 코드가 더 읽기 쉽고 유지보수하기 쉬워져요.
auto로 변수를 선언하는 다음 예시를 살펴봐요.
auto dragon_population{3}; // dragon_population is deduced as an integer
auto westeros{7.7777}; // westeros is deduced as a double
auto wedding_location{"The Twins"}; // wedding_location is deduced as a const char*, not std::string
각 경우에서 변수의 타입은 초기화에 사용된 값으로부터 추론돼요.
auto 키워드는 명시적인 타입이 길어지는 것을 줄여서 코드를 더 간결하고 읽기 쉽게 작성하도록 도와줘요.
const std::vector<std::string> pigeon_pie{"flour", "butter", "pigeon", "salt"};
auto purple_wedding_pie{pigeon_pie};
purple_wedding_pie.emplace_back("the strangler");
이 코드에서 auto는 purple_wedding_pie의 타입을 std::vector<std::string>으로 추론해서, 타입을 다시 명시적으로 지정할 필요가 없어요.
auto 키워드는 다양한 C++ 구문과 호환되어, 현대 C++ 프로그래밍에서 다재다능한 도구가 되어 줘요.
auto& element{array[0]}; // reference to an element
const auto object{otherObject}; // const type version of otherObject's type
auto* ptr{&x}; // pointer to x with the same type as x, but as a pointer.
이후 개념에서는 람다 표현식, 범위 기반 for문, 반복자와 함께 쓰이는 auto 키워드를 자주 보게 될 거예요.
스마트 포인터는 자동 메모리 관리를 제공하도록 설계된 현대 C++의 기능으로, 원시 포인터에서 흔히 발생하는 메모리 누수와 댕글링 포인터를 방지하는 데 도움을 줘요. 스마트 포인터는 원시 포인터를 감싸는 래퍼 역할을 하면서, 포인터가 더 이상 필요하지 않을 때 메모리를 자동으로 해제하는 등의 추가 기능을 더해 줘요.
스마트 포인터는 보통 C++ 표준 라이브러리에서 클래스 템플릿으로 구현돼 있어요.
가장 많이 사용되는 스마트 포인터 두 가지는 std::unique_ptr과 std::shared_ptr이에요.
std::unique_ptr은 객체를 독점적으로 소유하는 스마트 포인터예요.
어떤 시점에도 하나의 std::unique_ptr 객체만 리소스를 소유하도록 보장해요.
소유하고 있던 std::unique_ptr이 파괴되거나 리셋되면, 객체를 자동으로 소멸시키고 메모리를 해제해요.
#include <memory>
// Declaring and defining a unique pointer
auto rightful_king_of_england = std::make_unique<std::string>("Excalibur");
// Unique pointers cannot be copied or assigned
auto mordred = rightful_king_of_england; // Error: Cannot copy a unique_ptr
std::make_unique()의 장점std::unique_ptr을 만들 때는 new로 직접 메모리를 할당하는 대신 std::make_unique()를 사용하는 게 좋아요.
std::make_unique()는 여러 가지 장점을 제공해요.
std::make_unique()는 예외 안전성을 보장해요.
객체를 생성하는 도중 예외가 발생하면 메모리가 자동으로 해제되어 메모리 누수를 막아 줘요.std::make_unique()를 사용하면 코드가 더 명확하고 간결해져요.
템플릿 인자가 자동으로 추론되므로, 할당하는 타입을 명시적으로 지정할 필요가 없어요.new로 직접 메모리를 할당하는 것보다 std::make_unique()를 더 효과적으로 최적화할 수 있어서, 성능이 향상될 수 있어요.std::unique_ptr을 직접 생성하면 기반 리소스를 삭제하는 것이 가능해져요.
그러면 std::unique_ptr이 스코프를 벗어날 때 그 리소스를 삭제하려고 하면서 정의되지 않은 동작이 발생할 수 있어요.std::shared_ptr은 스마트 포인터로, 여러 std::shared_ptr 객체가 같은 리소스의 소유권을 공유할 수 있게 해 줘요.
리소스를 참조하는 공유 포인터가 몇 개인지 추적하다가, 그 리소스를 소유한 마지막 공유 포인터가 스코프를 벗어나거나 리셋될 때만 메모리를 해제해요.
// Declaring and defining a shared pointer to a dynamically allocated string
auto martian_congressional_republic = std::make_shared<std::string>("protomolecule");
// Creating more shared pointer that shares ownership
auto outer_planets_alliance = martian_congressional_republic;
auto united_nations = martian_congressional_republic;
C++17 이하에서는 std::make_shared<T[]>를 통해 배열과 함께 std::shared_ptr을 사용하는 것이 직접적으로 지원되지 않아요.
std::make_shared<T[]>로 배열을 할당하는 것은 가능하지만, 그 배열에서 바로 공유 포인터를 만들면 단일 객체와 배열의 메모리 관리 방식 차이 때문에 정의되지 않은 동작이 발생할 수 있어요.
대신, 공유 포인터로 배열을 효과적으로 관리하려면 std::vector나 사용자 정의 삭제자를 사용하는 것을 고려해 봐요.
C++17에서 배열 할당과 공유 포인터를 다룰 때는 항상 컴파일러와 표준 라이브러리 구현과의 호환성을 확인해요.
std::make_shared()의 장점std::make_unique()와 마찬가지로, std::make_shared()는 향상된 메모리 효율성, 예외 안전성, 가독성 같은 이점을 제공해요.
제어 블록과 관리 대상 객체에 대한 메모리 할당을 하나의 연산으로 합쳐서 효율성을 높이고 메모리 누수 위험을 줄여 줘요.
또한 템플릿 인자를 자동으로 추론해 주기 때문에 코드가 단순해지고 가독성이 좋아져요.
C++에서 std::shared_ptr 객체를 다룰 때 std::make_shared()를 사용하면 더 깔끔하고 안전하며 효율적인 코드를 작성할 수 있어요.
기본적으로 스마트 포인터를 사용해요. 독점적 소유에는 std::unique_ptr을, 공유 소유에는 std::shared_ptr을 사용해요.
원시 포인터는 소유하지 않는 참조나 레거시 코드와 연결할 때만 사용해요.
대부분의 경우 독점적 소유에는 std::unique_ptr로 충분한데, 참조 카운팅의 부담 없이 가벼운 메모리 관리를 제공하기 때문이에요.
std::shared_ptr은 진정한 공유 소유가 필요하지 않다면 부담과 복잡성을 더하기 때문에, 아껴서 사용해야 해요.
이 연습 문제는 트로이의 세계로 안내해요. 이곳 사람들의 삶은 경이와 마법으로 가득해요. 트로이의 많은 인간은 일상에서 자주 사용하는 _능력_을 지니고 있어요. 능력은 세계를 다시 빚어내거나 트로이의 동물과 다른 사람들에게 영향을 주는 데 사용돼요. 마법은 _고유한 아티팩트_의 형태로도 나타나는데, 모험가와 장인, 현자들이 이를 매우 탐내요.
이 연습 문제에서는 트로이의 인간과 그들이 지닌 아티팩트, 그리고 능력의 상호작용을 모델링하는 코드를 작성해요.
과제는 여섯 개예요. 첫 번째는 인간을 만드는 것에 관한 것이고, 나머지 다섯 개는 능력과 아티팩트를 다루는 것에 관한 거예요.
트로이 모델에서 인간은 가장 중요한 요소예요. 모델링한 인간은 _고유한 아티팩트_를 소유할 수 있어야 해요. 또한 _능력_을 발현할 수 있어야 해요. 이 능력은 다른 인간에게 영향을 줄 수 있으므로, 어떤 인간이 다른 능력의 영향을 받는지도 모델링해야 해요.
artifact와 power 구조체의 기본 구현이 제공돼요.
possession이라는 이름의 artifact 멤버 변수를 가리키는 _스마트 포인터_를 가진 human 구조체(또는 클래스)를 구현해요.
각 아티팩트는 어느 시점에든 단 한 명의 인간만 소유할 수 있어요.
human에는 멤버 변수가 두 개 더 있어야 해요.
하나는 자신의 own_power를 담고, 다른 하나는 자신이 influenced_by 관계에 있는 능력이에요.
own_power와 influenced_by는 모두 powers를 가리키는 _스마트 포인터_예요.
각 power는 한 명의 인간이 소유할 수 있지만, 동시에 다른 인간들에게도 영향을 줄 수 있어요.
기본적으로 인간은 아티팩트 없이 태어나며, 아무 능력도 소유하지 않고 어떤 영향도 받지 않아요.
human mindy_mccready{};
mindy_mccready.possession;
// => nullptr
mindy_mccready.own_power;
// => nullptr
mindy_mccready.influenced_by;
// => nullptr
모델의 각 요소가 상호작용하지 않으면 재미가 없어요. 고유한 아티팩트를 만들어 특정 인간에게 주고 싶어요.
아무것도 반환하지 않고 human에 대한 참조와 string을 받는 함수 give_new_artifact를 정의해요.
이 string으로 새로운 artifact 객체를 만들고, 그에 맞게 human의 possession 포인터를 설정해야 해요.
이 함수는 아무것도 반환해서는 안 돼요.
human erik_magnus_lehnsherr{};
give_new_artifact(erik_magnus_lehnsherr, "Mind shielding helmet");
erik_magnus_lehnsherr.possession->name;
// "Mind shielding helmet"
트로이의 세계는 상호작용이 전부예요. 사람들이 소유물을 교환하며 거래하기를 바라요.
아무것도 반환하지 않고 아이템을 교환할 아티팩트 스마트 포인터 두 개를 받는 함수 exchange_artifacts를 작성해요.
unique_ptr는 복사할 수 없다는 점을 기억해요.
함수 매개변수로 사용할 때도 마찬가지예요.
대신 unique_ptr에 대한 참조를 사용해요.
human uchiha{};
give_new_artifact(uchiha, "konoha headband");
human uzumaki{};
give_new_artifact(uzumaki, "forehead protector");
exchange_artifacts(uchiha.possession, uzumaki.possession);
uchiha.possession->name;
// "forehead protector"
uzumaki.possession->name;
// "konoha headband"
트로이에서 가장 흥미로운 점은 사람들이 지닐 수 있는 특별한 능력이에요. 어떤 이는 생각만으로 철을 제련하고, 또 어떤 이는 밤에 모든 상처를 즉시 치유할 수 있어요.
아무것도 반환하지 않고 human에 대한 참조와 string을 받는 함수 manifest_power를 정의해요.
이 string으로 새로운 power 객체를 만들고, 그에 맞게 human의 own_power 포인터를 설정해야 해요.
이 함수는 아무것도 반환해서는 안 돼요.
human eleven {};
manifest_power(eleven, "psychokinesis");
eleven.own_power->effect;
// "psychokinesis"
가장 위대한 능력도 사용할 수 없다면 무슨 소용이 있을까요. 모델은 인간에 초점을 맞추므로, 능력의 영향력을 추적하고 싶어요.
인간에 대한 참조 두 개를 받는 void 함수 use_power를 작성해요.
첫 번째 인간은 시전자이고, 두 번째는 대상이에요.
대상의 influenced_by 포인터가 시전자의 능력을 가리키도록 해야 해요.
간단하게 하기 위해, 인간은 단 하나의 능력에만 영향을 받을 수 있어요. 시전자가 더 이상 존재하지 않아도 이 능력은 그대로 남아 있어요.
human pamela_isley{};
manifest_power(pamela_isley, "control pheromones");
human count_vertigo{};
use_power(pamela_isley, count_vertigo);
count_vertigo.influenced_by->effect;
// "control pheromones"
특정 능력은 여러 인간에게 적용될 때 시뮬레이션에서 위력을 잃거나 특정 효과를 일으켜요. 각 능력에 연결된 사람의 수를 추적하고 싶어요.
인간을 받아 그 능력의 강도를 _int_로 반환하는 함수 power_intensity를 정의해요.
그 사람에게 능력이 없으면 반환 값은 0이어야 해요.
그렇지 않으면 강도는 시전자와 현재 영향받고 있는 모든 사람을 반영해야 해요.
human jean_grey{};
manifest_power(jean_grey, "uplifting personality");
human scott{};
human logan{};
human ororo{};
use_power(jean_grey, ororo);
use_power(jean_grey, logan);
use_power(jean_grey, scott);
power_intensity(jean_grey);
// 4
Exercism에 가입하고 C++ 트랙을 개념 19개연습 문제 100개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.