강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

Sung님의 프로필 이미지
Sung

작성한 질문수

홍정모의 따라하며 배우는 C++

14.3 예외 클래스와 상속

5분 17~5분 34초 객체 잘림 질문

작성

·

13

1

안녕하세요. 저도 객체 잘림에 대한 의문이 들어 durams 님의 질문을 보고 나서 나름대로 정리를 해보고 있는데

 

(durams 님이 질문에 작성하는 코드)

class MyArray {
private:
	int m_data[5];

public:
	int& operator [](const int& index) {
		if (index < 0 || 5 <= index) {
			ArrayException e;
			cout << &e << endl;
			throw e;
		}
		return m_data[index];
	}
};

void doSomething() {
	MyArray my_array;

	try {
		my_array[100];
	}
	catch (Exception& e) {
		cout << "doSomething()" << endl;
		cout << &e << endl;
		e.report();
		throw e;
	}
}

int main() {
	try {
		doSomething();
	}
	catch (Exception& e) {
		cout << "main()" << endl;
		cout << &e << endl;
		e.report();
	}

	return 0;
}

 

catch (Exception& e) {
	cout << "doSomething()" << endl;
	cout <<"catch 참조 변수 주소: " << &e << endl;
	e.report();

	throw e;
}

위의 코드를 설명해보면, (Exception& e)는 throw된 객체의 참조이고 위의 throw e;의 e는 throw 될 때 e가 참조하고 있는 변수를 '복사 생성'한 독립적인 객체다 또한 'throw;될 경우 복사 생성하지 않고, catch에서 잡은 예외 객체를 그대로 다시 던지는 것'이다

 

즉, catch의 &는 부모 type의 매개 변수를 참조 없이 선언하여 객체 잘림이 될 경우를 예방하는 것이다.

위의 설명들이 맞을까요?

그렇다면 5 분 17초에서 임시 객체를 throw하고 있고 매개 변수는 참조의 형태를 가지고 있기 때문에 객체 잘림이라는 설명은 맞지 않고 다형성의 자식의 객체를 부모의 참조로 조작하는 경우인 것 아닌가요?

 

답변 1

0

안녕하세요? 질문&답변 도우미 durams입니다.

제가 예전에 썼던 글을 잘 봐주셨네요. '위의 설명들'은 맞는 내용입니다. 다만 언급해주신 5분 17초부터의 내용에 대해서는 조금 설명이 필요합니다.


그렇다면 5 분 17초에서 임시 객체를 throw하고 있고 매개 변수는 참조의 형태를 가지고 있기 때문에 객체 잘림이라는 설명은 맞지 않고 다형성의 자식의 객체를 부모의 참조로 조작하는 경우인 것 아닌가요?

: 아마 영상의 해당 부분에서 report 함수의 출력이 Exception report로 나온 이유가 객체 잘림 때문이 아닌 것 같아서 의문을 표하시는 것으로 보입니다.

저도 질문자님의 글을 보고 영상을 다시 보면서 찾은 사실이지만, 결론부터 말씀드리자면 강의에 미묘한 오류가 있습니다. 제가 이전에 작성했던 글을 보셨으니 알고 계실 테지만, catch문에서 객체를 &(reference)로 잡았기 때문에 오히려 object slicing는 제대로 방지되고 있습니다.

해당 부분에서 Array exception이 아닌 Exception report가 출력된 이유는 report 함수가 오버라이딩되지 않았기 때문입니다. 강의에서 사용하는 코드를 잠깐 보시면 아래와 같습니다.

class Exception
{
public:
        void report()
	{
		cerr << "Exception report" << endl;
	}
};

class ArrayException : public Exception
{
public:
	void report()
	{
		cerr << "Array Exception" << endl;
	}
};

강의에서도 report 함수를 오버라이딩했다고 소개하지만, 사실 이건 오바라이딩이 아닌 아무 관계가 없는 별개의 함수 정의라고 봐야합니다. 왜냐하면 Exceptionreportvirtual 키워드가 없기 때문입니다. 즉 다형성이 동작하지 않습니다. 런타임 다형성을 위한 v-table 정보가 생성되지 않으며, ArrayException 객체를 Exception& 에 할당해서 report를 호출하더라도 단순히 Exception::report가 호출됩니다.

즉, 강의 코드대로 작성하면 catch(Exception& e)처럼 object slicing을 방지하도록 catch문을 작성한 것과 관계없이 애초에 런타임 다형성이 동작하는 형태가 아니기 때문에 Exception의 참조에서 호출한 report는 언제나 Exception report를 출력하게 됩니다.

해결 방법은 진짜로 오버라이딩을 하면 됩니다.

class Exception
{
public:
	virtual void report()
	{
		cerr << "Exception report" << endl;
	}
};

class ArrayException : public Exception
{
public:
	void report() override
	{
		cerr << "Array Exception" << endl;
	}
};

이렇게 하면 질문글에 첨부해주신 제 코드의 doSomething 내에서 catch했을 때 제대로 Array Exception report이 출력됩니다. 대신 main에서 catch해서 나오는 출력문은 그대로 Exception report인데요, 이건 그런지 한 번 생각해보셨으면 좋겠습니다.

질문은 이어서 주셔도 됩니다.

Sung님의 프로필 이미지
Sung

작성한 질문수

질문하기