C++ Chapter 9.11 : 변환 생성자, explicit, delete

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!


chapter 9. 연산자 오버로딩 : 변환 생성자, explicit, delete

생성자를 명확히 호출하고 쓸데 없는 호출들을 막기 위한 방법!

🔔 변환 생성자

#include <iostream>
#include <cassert>
using namespace std;

class Fraction
{
private:
	int m_numerator;
	int m_denominator; 

public:
	Fraction(int num = 0, int den = 1)
		: m_numerator(num), m_denominator(den)
	{
		assert(den != 0); 
	}
	
	Fraction(const Fraction &fraction)
		:m_numerator(fraction.m_numerator), m_denominator(fraction.m_denominator)
	{ 
		cout << "Copy constructor called" << endl; 
	}

	friend std::ostream & operator << (std::ostream & out, const Fraction & f)
	{
		out << f.m_numerator << " / " << f.m_denominator << endl;
		return out;
	}
};

void doSomething(Fraction frac)
{
	cout << frac << endl;
}

int main()
{
	doSomething(7);

	return 0;
}
  • Fraction(int num = 0, int den = 1)
    • 생성자
    • 인수 0개 받으면 👉 Fration(0, 1)
    • 인수 1개 받으면 👉 Fration(받은 인수, 1)
    • 인수 2개 받으면 👉 Fration(받은 인수, 받은 인수)
  • void doSomething(Fraction frac)
    • 이 함수는 Fraction 타입의 객체를 인수로 받는다.
doSomething(7); // 문제 없음!  doSomething(Fracion(7, 1)); 이나 마찬가지다.
  • 위 함수는 Fraction 타입의 객체를 인수로 받는데도 불구하고 int인 7을 인수로 넘겨도 아무 문제가 없다.
    • 7이 인수로 넘겨질 때 함수 내부의 매개변수 객체인 frac이 생성되면서 Fraction frac = 7
      • Fraction(int num = 0, int den = 1) 생성자가 호출되어 Fraction 타입의 임시 객체가 만들었졌기 때문에 문제가 없는 것이다.
        • 인수로 7 하나만 받았기 때문에 den 멤버는 디폴트 값은 1로 초기화되어 최종적으로 “Fracion(7, 1)”로 호출한 것이나 마찬가지인 Fraction 타입의 임시객체가 생성된다.

          이처럼 기본 자료형 1개를 인수로 받는 생성자는 컴파일러가 자동 형변환을 해주기 때문에 이러한 특이 현상이 일어날 수 있다. 이런 현상이 생기는 생성자를 변환 생성자라고 한다.

        • 인수를 int 로 잘못 넘겼음에도 불구하고 컴파일러가 int 하나를 받는 생성자를 호출하여 Fraction 타입의 임시 객체로 자동 형변환 해준 셈.
        • 위 생성자는 int 인수 하나만 들어오면 무조건 OK라 이런 문제가 발생함 😥

변환 생성자의 좋지 않은 점

  1. 논리성이 떨어진다.
    • 특정 타입의 객체를 인수로 받는다고 명시해놨음에도 불구하고 컴파일러가 자동 형변환을 해줘서 int 로 넘겨도 문제가 없으니까!
    • 실수로 int 로 변환될 수 있는 char 같은 것을 넘겨도 컴파일러가 자동 형변환을 해주므로 변환 생성자가 호출되는 문제가 발생한다.
  2. 메모리 낭비다.
    • int로 넘겨도 변환 생성자를 호출하는 바람에 임시 객체를 생성하고 소멸하는 과정이 따르기 때문


🔔 생성자 명확히 호출하기

explicit

생성자 앞에 explicit 키워드를 붙여주면 변환 생성자의 무작위 호출을 막고 명확성을 높여준다.

explicit Fraction(int num = 0, int den = 1)
	: m_numerator(num), m_denominator(den)
{
	assert(den != 0); 
}
doSomething(7); // 💥컴파일러 에러 발생

인수로 넘긴 7이 Fraction(7, 1)로 호출되는 것을 막고 컴파일러 에러를 발생시킨다. 이제 doSomething(Fraction(7, 1)); 혹은 doSomething(frac); 하고 타입을 명확히 해서 호출해주어야 한다.


delete

생성자에 delete키워드를 붙여 특정 타입의 인수는 받지 않도록 막을 수 있다. 이때의 delete는 동적 할당 받은 메모리를 해제 시킬 때 쓰는 그 delete와는 다르다.

Fraction(const Fraction & frac) = delete;
  • 복사 생성자 구현안한 상태인데 디폴트 복사 생성자를 호출하고 싶지 않다면 위와 같은 방식으로 호출을 막을 수도 있다.
Fraction(char) = delete;

...
Fraction frac('c');  // 💥컴파일러 에러 발생
  • 인수 1개를 받는 생성자 호출시 char 타입의 인수는 받지 않도록 막았다.
#include <iostream>
#include <cassert>
using namespace std;

class Fraction
{
private:
	int m_numerator;
	int m_denominator; 

public:
    Fraction(char) = delete;
	explicit Fraction(int num = 0, int den = 1)
		: m_numerator(num), m_denominator(den)
	{
		assert(den != 0); 
	}
	
	Fraction(const Fraction &fraction)
		:m_numerator(fraction.m_numerator), m_denominator(fraction.m_denominator)
	{ 
		cout << "Copy constructor called" << endl; 
	}

	friend std::ostream & operator << (std::ostream & out, const Fraction & f)
	{
		out << f.m_numerator << " / " << f.m_denominator << endl;
		return out;
	}
};

void doSomething(Fraction frac)
{
	cout << frac << endl;
}

int main()
{
	doSomething(7);     // 💥컴파일러 에러 발생
    Fraction frac('c');  // 💥컴파일러 에러 발생

	return 0;
}


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기


Cpp 카테고리 내 다른 글 보러가기

댓글 남기기