[C++/Classes] Inherited Code

kkanyo·2022년 1월 9일

문제


Link: Inherited Code | HackerRank

exception 클래스를 상속받는 BadLengthException 클래스를 구현하여 사용자명을 입력받고 조건에 부합할 때 발생하는 예외를 처리하는 문제입니다.


풀이


class BadLengthException : public exception {
    private:
        string errstr;
        stringstream ss;	//Caution
	...
    
    ...
    
    } catch(BadLengthException e) //Compile error

stringstream 객체를 BadLengthException 클래스의 변수로 선언하면 catch 문에서
컴파일 오류가 발생합니다.

checkUsername 에서 예외가 발생하면서 BadLengthException 객체를 throw합니다.

그리고 catch 문의 매개변수는 BadLengthException 객체를 전달 받는데, throw된 예외의 복사 생성자에 의해 복사된 객체를 인수로 전달받습니다.

문제는 디폴트 복사 생성자로 인한 복사 방식이 얕은 복사(Shallow Copy)라는 것에서 발생합니다.

기존 객체와 복사된 객체가 동일한 stringstream 객체 ss 의 메모리 주소를 참조하게 됩니다.

throw 후에 기존 BadLengthException 객체는 소멸자를 호출하고, 멤버 변수인 ss 도 소멸자를 호출하며 메모리가 해제됩니다.

그런데 여기서 catch 문의 수행이 끝나면 복사된 BadLengthException 객체가 다시 한 번 소멸자를 호출하게 되는데 이미 ss 는 소멸되었으므로, 오류가 발생합니다.

따라서, stringstream을 사용하는 것이 아닌 to_string을 이용하여 변환하는 것이 안전합니다.


	...
    
    	BadLengthException(int n) {
            stringstream ss;
            ss << n;
            errstr = ss.str();
        }
      	const char * what() const noexcept {
            return errstr.c_str();
            /*
            stringstream ss;
            ss << errnum;

            return ss.c_str()
            -> This is dangerous!
            */
        }
        
        ...

what 함수는 exception 클래스를 public 접근자로 상속받아 오버라이딩(Oberriding)으로 구현했습니다.

stringstream 객체, sswhat 함수 블록 내에서 선언되었습니다.

함수의 실행이 종료되면 메모리 상에서 사라지기 때문에 ss 를 이용하여 값을 반환하는 것은 메모리 누수 위험이 존재하는 방법입니다.

따라서, 상속자를 통해 ss 를 선언하여 username의 길이를 문자열로 변환해준 뒤, errstr 문자열 변수에 저장해두어 사용했습니다.


전체 코드


#include <iostream>
#include <string>
#include <sstream>
#include <exception>
using namespace std;

/* Define the exception here */
class BadLengthException : public exception {
    private:
        string errstr;
    public:
        BadLengthException(int n) {
            stringstream ss;
            ss << n;
            errstr = ss.str();
            //errstr = to_string(n);
        }
        const char * what() const noexcept {
            return errstr.c_str();
        }
};

bool checkUsername(string username) {
	bool isValid = true;
	int n = username.length();
	if(n < 5) {
		throw BadLengthException(n);
	}
	for(int i = 0; i < n-1; i++) {
		if(username[i] == 'w' && username[i+1] == 'w') {
			isValid = false;
		}
	}
	return isValid;
}

int main() {
	int T; cin >> T;
	while(T--) {
		string username;
		cin >> username;
		try {
			bool isValid = checkUsername(username);
			if(isValid) {
				cout << "Valid" << '\n';
			} else {
				cout << "Invalid" << '\n';
			}
		} catch (BadLengthException e) {
			cout << "Too short: " << e.what() << '\n';
		}
	}
	return 0;
}

참고


profile
"Engineering is the closest thing to magic that exists in the world." - Elon Musk

0개의 댓글