작성
·
33
·
수정됨
1
교수님의 강의를 보고 선 정리를 하는 과정에서 chat gpt를 이용해서 새로운 예제를 같이 공부하고 있습니다.
교수님의 코드에서는 ArrayException 객체를 throw하는 과정에서 메모리 문제가 발생하지 않고 부모 객체로 catch하여
ObjectSilce 문제를 확인을 할 수 있었는데요.
chat gpt가 알려준 예제(예제 클래스 상속으로 인한 Object Slice 문제)
#include <iostream>
#include <string>
// 사용자 정의 예외의 기본 클래스
class Exception {
public:
explicit Exception(const std::string& message) : message_(message) {}
// 가상 소멸자 추가
virtual ~Exception() {}
// 예외 메시지를 반환하는 가상 함수
virtual const char* what() const noexcept {
return message_.c_str();
}
private:
std::string message_;
};
// Exception을 상속받은 커스텀 예외 클래스
class CustomException : public Exception {
public:
explicit CustomException(const std::string& message, int errorCode)
: Exception(message), errorCode_(errorCode) {}
// CustomException에만 있는 멤버 함수
int getErrorCode() const {
return errorCode_;
}
private:
int errorCode_;
};
void throwException() {
// CustomException 객체를 값으로 던짐 (복사본이 던져짐)
CustomException ex("CustomException occurred", 404);
throw ex; // 함수 종료 시 스택에서 삭제될 객체의 복사본이 던져짐
}
int main() {
try {
throwException();
}
catch (Exception& e) { // Exception을 값으로 잡음 -> 객체 잘림 발생
std::cout << e.what() << std::endl;
// 파생 클래스의 멤버 접근 시도
try {
CustomException& ce = dynamic_cast<CustomException&>(e);
std::cout << "Error code: " << ce.getErrorCode() << std::endl;
} catch (std::bad_cast&) {
// 객체 잘림으로 인해 CustomException으로 캐스팅 실패
std::cout << "Caught bad_cast: Object slicing occurred, cannot cast to CustomException." << std::endl;
}
}
return 0;
}
위 코드를 실행 시켰을때 예외 클래스를 throw 해줄 때 '메모리 문제'가 발생 하더라구요.
그리고 부모 클래스로 catch를 하지 못했습니다.
그래서 저는 교수님 코드와 다른게 뭘까 하고 생각해 보다가 예외클래스 객체의 생성주기의 문제라고 생각해서 gpt와 대화(?)를 해보았고 생성주기 부분을 수정해 주니 정상적으로 작동하였습니다.
아래의 예제 코드처럼 동적 할당으로 예외 클래스를 만들어주는게 무겁지 않을까 생각이 듭니다.
혹시 저 위 코드에서 메모리 문제를 해결하는 방법이 동적 할당 밖에 없는지 궁금합니다.
예외 클래스를 동적 할당으로 생성하여 throw해준 예제 코드
#include <iostream>
#include <string>
// 사용자 정의 예외의 기본 클래스
class Exception {
public:
explicit Exception(const std::string& message) : message_(message) {}
// 가상 소멸자를 추가해 다형성 지원
virtual ~Exception() {}
// 예외 메시지를 반환하는 가상 함수
virtual const char* what() const noexcept {
return message_.c_str();
}
private:
std::string message_;
};
// Exception을 상속받은 커스텀 예외 클래스
class CustomException : public Exception {
public:
explicit CustomException(const std::string& message, int errorCode)
: Exception(message), errorCode_(errorCode) {}
// CustomException에만 있는 멤버 함수
int getErrorCode() const {
return errorCode_;
}
private:
int errorCode_;
};
void throwException() {
// 예외 객체를 동적으로 생성하여 포인터로 던짐 (메모리 관리 필요)
throw new CustomException("CustomException occurred", 404);
}
int main2() {
try {
throwException();
}
// 포인터로 던진 예외를 잡음
catch (Exception* e) {
std::cout << e->what() << std::endl;
// 파생 클래스로 dynamic_cast하여 멤버 접근 시도
if (CustomException* ce = dynamic_cast<CustomException*>(e)) {
std::cout << "Error code: " << ce->getErrorCode() << std::endl;
}
else {
std::cout << "Caught base Exception, cannot cast to CustomException." << std::endl;
}
// 동적으로 할당한 예외 객체 해제
delete e;
}
return 0;
}
답변 2
2
안녕하세요? 질문&답변 도우미 Soobak 입니다.
흥미로운 질문이시네요.
다만, 제 환경에서는 첫 번째 코드 또한 정상적으로 동작합니다.dynamic_cast<>
도 정상적으로 잘 동작하여 자식 클래스에 있는 에러 코드를 잘 출력합니다.
(실행 화면)
메모리 관련 검사 또한 해보았으나, 아쉽게도 문제가 없었습니다.
어떠한 문제가 발생한 것이신지 조금 더 구체적으로 알려주시면 제가 더 적절한 도움을 드릴 수 있을 것 같습니다.
그럼에도 질문주신 내용에 대해서 답변을 드려보자면, 예외 객체를 동적으로 할당하여 throw
하는 경우는 예외가 적절히 catch
되지 않으면 메모리가 해제되지 않아 메모리 문제가 생길 수 있게 됩니다.
또한, 예외를 관리하는 데에 어려움이 증가할 것 같습니다.
스택에서 직접 생성된 예외 객체는 예외가 포착되지 않더라도 자동으로 메모리가 해제되므로,
개인적으로는 try
이후 catch
구문의 순서를 적절히 조절하는 것, 그리고 예외 객체를 던지고 받을 때 상속 구조를 유의하여 값-참조 자료형을 적절히 선택하고 관리하는 방향을 더 선호할 것 같습니다.
개인적으로는 dynamic_cast<>
를 사용하지 않고, 다음과 같이 작성할 것 같습니다.
#include <iostream>
#include <string>
class Exception {
public:
explicit Exception(const std::string& message) : message_(message) {}
virtual ~Exception() {}
virtual const char* what() const noexcept {
return message_.c_str();
}
private:
std::string message_;
};
class CustomException : public Exception {
public:
explicit CustomException(const std::string& message, int errorCode)
: Exception(message), errorCode_(errorCode) {}
int getErrorCode() const {
return errorCode_;
}
private:
int errorCode_;
};
void throwException() {
CustomException ex("CustomException occurred", 404);
throw ex;
}
int main() {
try {
throwException();
}
catch (const CustomException& e) { // CustomException을 직접 catch
std::cout << e.what() << std::endl;
std::cout << "Error code: " << e.getErrorCode() << std::endl;
}
catch (const Exception& e) {
std::cout << e.what() << std::endl;
}
return 0;
}
다만, 추후 섹션 16 에서 학습하시게 되는 스마트 포인터를 이용하신다면 메모리 관리에 조금 덜 신경쓰며 동적으로 예외 객체를 할당하여 사용하는 방법도 있겠네요.
하지만, 그럼에도 불구하고 여러가지 측면을 고려했을 때 개인적으로 예외 객체를 동적으로 할당하는 것은 지양하는 것이 좋다고 생각합니다.
고민해볼 수 있는 재미있는 내용이네요.
항상 열정있는 학습과 재미있는 질문 감사합니다. 저에게도 좋은 자극이 되어 주시고 계십니다.
궁금한 점이 있으시면 편하게 질문해주세요!
글을 통해서 대화를 나누는 역량 그 자체는 저도 열심히 신경쓰며 키워나가고 있는 부분이지만,
질문의 내용이나 주제에 대해서는 궁금증이 생기실 때 편하게 작성해주시는 것이 좋은 것 같습니다.
안녕하세요? 질문&답변 도우미 Soobak 입니다.
예외를 동적 할당으로 던져주는 방식의 장/단점을 떠나서, 학습 단계에서 문제의 원인을 추측해보시고 해결 방법을 떠올리시는 것이 훌륭하신 것 같습니다.
저 또한 떠올리지 못했던 생각이고, 덕분에 재미있게 답변을 드릴 수 있었습니다.
이번에는 다른 컴퓨터를 사용해서도 컴파일을 진행해보고, 디버깅 또한 해보았지만 정상적으로 동작을 하네요. 😭
이전에 주셨던 질문들과 관련하여 환경 설정을 알아보실 때, Visual Studio
의 Solution
과 Project
의 차이점, 그리고 빌드 구성을 키워드로 학습해보시면 도움이 되실 것 같습니다.
명확한 도움을 드리지 못해서 아쉽네요.
또한 이 곳은 따배씨++ 의 학습 장소이므로 개인적인 조언을 드리기는 어려울 것 같습니다.
정중히 양해를 부탁드립니다.
다만, 교수님께서 운영하시는 디스코드 커뮤니티 https://discord.gg/GYrE4XEz 에 저 뿐만 아니라, 보다 훌륭하신 현직자 분들도 참여하고 계시니, 조언이 필요하신 부분을 커뮤니티에서 질문해보시면 질문자님께 큰 도움이 되실 것 같습니다.
개인적인 질문 드린다는 부분은 여기서 물어보면 안될 것 같아 지웠는데 보셨군요... ㅡㅜ
멘토 같이 soobak님을 생각 하다 보니 잘 못된 판단을 했던 것 같습니다! ㅎㅎ(죄송하네요)
말씀 하셨던 것과 같이 환경설정 관련해서 알아볼 예정이었는데,
조언을 해주셔서 감사합니다!
알려주신 키워드 바탕으로 학습해 보고선 환경 설정을 다시 해보도록 하겠습니다.
좋은 답변들을 주셔서 항상 답변을 기다리게 되네요.
좋은 주말 보내세요~
저만 안되는 것 같아 솔루션을 새로 만들어서 테스트해 보니
정상적으로 빌드 되더라구요..ㅡㅜ
문제 파악하느라 날린 시간이 아깝지만 수박님 덕분에 문제의 해결점을 하나 더 찾았네요.
감사합니다~ ㅎㅎ 😄
0
안녕하세요, 인프런 AI 인턴입니다. 호두님, 질문해주신 예외 클래스의 상속 및 객체 생성 주기 문제에 대해 살펴보겠습니다.
먼저, 첫 번째 예제에서 문제가 발생하는 이유를 살펴보겠습니다. Exception
클래스를 부모 클래스로 catch
할 때, Exception e
와 같이 값을 통해 객체를 받게 됩니다. 이 경우 객체 잘림(Object Slicing) 문제가 발생합니다. 즉, CustomException
이 Exception
으로 잘리면서 CustomException
의 고유 정보를 잃어버리게 됩니다. 이러한 상황에서는 dynamic_cast
를 통해 CustomException
으로 캐스팅하려고 할 때 문제가 발생할 수 있습니다.
두 번째 방법으로 예외 객체를 동적으로 할당하는 방법은 잘못된 객체 잘림을 피할 수 있으나, 예외 객체의 동적 할당은 권장되지 않습니다. 이는 예외 객체의 생명 주기를 관리하는 데 복잡성을 가져오고, 메모리 누수 위험을 초래할 수 있기 때문입니다.
catch
하기해결책은 간단합니다. 객체를 참조를 사용하여 catch
블록에서 받는 것입니다.
catch (Exception& e) {
// 처리 로직
}
이렇게 하면 객체 잘림 없이 throw
된 예외 객체를 온전히 받을 수 있게 됩니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
이번에도 열심히 답변을 주셔서 감사합니다.
제 컴파일러 셋팅의 문제인지 저의 경우에 첫 예제를 빌드 시 아래와 같은 문제가 발생하네요.
위 같은 문제가 발생해서 두 가지 방법으로 해결이 되었는데요.
catch에 해당 타입을 넣어주기(하지만 상속으로 인한 Object-Slide 문제를 확인 하기위해선 적절하지 않은 방법이라고 생각했습니다.
동적 할당을 통해 예외 객체를 살려서 던져주기를 하였습니다.
답변을 주셨던 것 처럼 동적 할당을 하면 '메모리 누수'가 발생 하기도 쉽거니와
예외 처리를 하는데 있어 무거워지지 않을까 걱정이 들어 질문을 하게 되었습니다.
(말씀 하신 smart-pointer에 대해서 이제 공부 중이네요 ㅎㅎ 적용하면 좋을 듯 한데
얘기하신 것 처럼 동적으로 할당하는 것은 지양 하도록 하겠습니다.)
저번 부터 제쪽에 환경 설정이 문제가 되어 공부의 흐름이 끊기게 되는 것 같네요.
환경 설정에 대해 좀 알아보고 손을 봐야 할 것 같습니다.