Animal create_new_anial() {
std::cout << CREATE_ANIMAL_MSG << std::endl;
Animal new_animal;
std::string temp_name;
while (true) {
std::cout << "Name of new animal (1~30 chars): ";
std::getline(std::cin >> std::ws, temp_name);
if (temp_name.length() >= 1 &&
temp_name.length() <= 30) {
std::strncpy(new_animal.name, temp_name.c_str(), sizeof(new_animal.name) - 1);
new_animal.name[sizeof(new_animal.name) - 1] = '\0';
break;
}
std::cout << "Name must be between 1 and 30 characters.\n";
}
std::cout << "how old is you animal : ";
std::cin >> new_animal.age;
new_animal.food = DEFAULT_FOOD;
new_animal.clean = DEFAULT_CLEAN;
new_animal.health = DEFAULT_HEALTH;
return new_animal;
}
list[animal_count++] = &create_new_anial();
create_new_animal 함수가 지역 스코프에서 구조체 new_animal을 생성한다. 이를 리턴하였고 이것을 list배열이 받아 참조한다.
내가 생각하기로 Animal 타입으로 레퍼런스가 아닌 인스턴스를 그대로 리턴하면 복사가 수행되어 안전할 것이라고 판단했다.
Aniaml animal = create_new_animal();
이렇게 처리한다면 안전하지만 앞에 &를 붙이면 cpp 표준에서 적용 불가하다고 한다.
함수 반환값은 임시 객체인 prvalue이며, 컴파일러가 다음과 같은 오류를 낼 수 있다.
error: taking the address of a temporary object
임시 객체는 위 반환되는 라인 이후로 파괴되고 list의 포인터는 dangling pointer가 된다.
이로인해 발생 가능한 문제는 다음과 같다.
Cpp에서 값의 종류
1. lvalue = 이름이 있는 객체, 주소를 가질 수 있다.
2. xvalue = 곧 소멸될 객체, 주소를 가질 수 있다. (실제 객체이지만 이동(move) 대상이 되는 객체.)
3. prvalue = 순수한 값(pure rvalue), 주소를 가질 수 없다. (계산 결과나 함수 반환 같은 순수한 값)10; &10; // 불가. 이런게 prvaluemove는 std::move() 함수 호출로 이 객체를 더이상 사용하지 않으니 컴파일러에게 회수하라고 알려주는 역할 이동 생성자가 호출되고, 메모리 복사 없이 포인터를 넘겨줌.
std::string s1 = "Hello"; std::string s2 = std::move(s1); std::cout << s1;마지막줄 s1은 어떤 값인지 보장되지 않음.
결국 대입할 때 std::move() 호출해주면 되며, 실제 이동이 발생하는지는 내부에 저장된 값에 따라 컴파일러가 판단함.
std::vector<Animal> list;
list.reserve(MAX_ANIMAL); // 선택사항
list.push_back(create_new_animal()); // 내부에서 복사 저장, 안전
Animal * list[MAX_ANIMAL];
Animal temp = create_new_animal();
list[animal_count++] = new Animal(temp); // 힙에 복사본 생성
프로그램 종료 전 반드시 해제 필요:
for (int i = 0; i < animal_count; i++) {
delete list[i];
}