[ c++ ] 클래스, 생성자, 소멸자, 이니셜라이저, this포인터

개발하는 곰댕이·2021년 12월 17일
0

cpp

목록 보기
2/11

클래스

우리는 c에서 구조체를 자주 사용했었습니다.
이 구조체는 클래스의 일종으로써 c에서 구조체를 잘 사용했다면 큰 문제없이 이해할 수 있을거에요.

클래스와 구조체의 차이점

구조체는 클래스의 일종이라고 했는데 그러면 둘의 차이는 무엇이 있을까요?

  • 선언이 다르다.
    구조체는 struct로 선언해서 사용했는데 클래스는 class로 선언해서 사용합니다.
class	A
{
	int num;
    
	void	func(void);
};
  • 접근제어 지시자의 차이
    클래스를 사용할 때 접근제어 지시자라는게 있는 데 다음과 같이 세 가지가 존재합니다.
    • private
      클래스의 멤버함수에서만 접근이 가능합니다.
    • protected
      상속관계에 있을 때, 유도 클래스에서 접근이 가능합니다.
    • public
      어디서든지 접근이 가능합니다.

따로 선언을 해 주지 않는다면 모두 private으로 생성되게 됩니다.

선언은 다음과 같이 합니다.

class	A
{
private:
	int _num;

public:
	void	func(void);
};

물론 구조체에서도 접근제어 지시자의 선언이 가능하다고 합니다.
하지만 구조체는 따로 선언을 하지 않는다면 모두 public으로 선언이 되고, 클래스에서는 모두 private으로 선언이 됩니다.

그렇기 때문에 다음과 같은 클래스의 초기화는 불가능합니다.

int	main(void)
{
	A a = {1};
	return (0);
}

왜냐하면 변수 _num은 현재 private으로 선언되어 외부에서는 접근이 불가능하기 때문입니다.

이 변수에 접근하기 위해서는 public으로 선언된 func함수 내부에서 접근이 가능합니다.

혹은 생성자라고 하는 녀석을 이용해서 초기화가 가능합니다.

생성자

이전에도 말 했듯 private 변수는 외부에서는 접근이 불가능하기 때문에 외부에서는 초기화가 불가능합니다.
하지만 생성자를 이용한다면 객체를 생성함과 동시에 초기화가 가능합니다.
클래스의 정의는 다음과 같습니다.

class	TempA
{
private:
	int	_num;

public:
	TempA(void);
};

public에 선언된 TempA가 생성자입니다.
생성자는 꼭 하나 이상의 생성자가 있어야 하기 때문에 객체가 생성될 때 접근 가능한 생성자가 선언되어 있지 않다면 자동으로 생성됩니다.
이렇게 생성된 생성자를 default 생성자라고 합니다.
만약 생성자가 하나라도 있다면 default 생성자는 생성되지 않습니다.

위 내용을 보면 생성자의 특징은 다음과 같습니다.

  • 반환형이 선언되어있지 않습니다.
  • 클래스의 이름동일한 함수명을 갖고있습니다.

이러한 함수를 우리는 생성자라고 합니다.

이런 생성자는 초기화할 때 매개변수를 받아서 초기화를 할 수 있도 있습니다.
정의는 다음과 같이 합니다.

TempA(int num)
{
	_num = num;
}
  • 객체를 생성할 때는 다음과 같이 생성합니다.
TempA a;
TempA b(1); //생성자에 1을 전달
TempA *c = new TempA; //동적할당
TempA *d = new TempA(2)
  • 객체를 생성할 때 다음과 같은 선언은 주의해야합니다.
TempA a(); // 생성자 호출?
TempA b = new TempA() // 생성자를 통한 동적할당?

위 내용에서 첫 번째 선언은 불가능하고 두 번째 생성은 가능합니다.
그 이유는 첫 번째 선언은 TempA의 반환형을 가진 a라는 함수의 선언이라는 뜻 또한 가질 수 있기 때문에 사용할 수 없습니다.

하지만 두 번째 선언은 함수의 선언과는 다르기 때문에 사용이 가능합니다.

멤버 이니셜라이저(초기화리스트)

클래스에는 멤버 이니셜라이저라는 녀석이 있는데 생성자를 사용할 때 초기화를 도와주는 기능입니다.
선언은 다음과 같이 합니다.

class	TempA
{
private:
	int	_num;

public:
	TempA(void):_num(1){}
};

기존과는 다르게 :을 이용해서 추가된 부분이 있습니다.
뜻은 멤버변수 _num을 1로 초기화 해라라는 뜻입니다.

만약 매개변수가 있는 생성자를 이용한다면 괄호 안에 매개변수를 넣어주면 됩니다.

이러한 이니셜라이저를 통한 초기화의 이점은 다음과 같습니다.

  • 초기화의 대상이 명확하게 인식됩니다.
  • 성능의 약간의 이점이 있습니다.
  • const 상수 및 참조자를 선언할 수 있게 해줍니다.

이니셜라이저를 통한 초기화와 몸체에서의 초기화를 코드로 비유하면 다음과 같습니다.

int _num = 1; // 이니셜라이저를 통한 초기화

int _num; // 몸체에서 초기화
_num = 1;

그렇기 때문에 선언과 동시에 초기화가 되어야하는 const 상수나 참조자도 멤버변수로 사용할 수 있게 됩니다.

소멸자

소멸자는 생성자와는 반대로 객체가 소멸할 때 자동으로 호출되는 녀석입니다.
소멸자는 다음과 같은 특징을 갖고 있습니다.

  • ~로 시작되고 클래스 이름과 같은 함수명을 갖습니다.
    ~TampA(void)와 같은 모양을 합니다.
  • 생성자와 마찬가지로 반환형이 없습니다.
  • 생성자와는 다르게 매개변수를 받을 수 없기 때문에 오버로딩, 디폴트 값 설정도 할 수 없습니다.

이런 소멸자는 함수 몸체에 정의된 내용을 객체가 소멸할 때 자동으로 실행합니다.

보통 소멸자는 생성자에서 할당한 리소스의 소멸에 사용됩니다.
예를 들어

TempA(void)
{
	_num = new int;
}

라고 생성자에서 정의되어 있다면

~TempA(void)
{
	delete	_num;
}

다음과 같이 정의해서 할당된 메모리를 해제합니다.

this포인터

this포인터객체 자기 자신의 주소를 가리키는 용도입니다.
주로 사용되는 곳은 다음과 같습니다.

  • 멤버변수와 매개변수의 변수명이 동일할 때
  • 자기 자신의 참조값을 리턴할 때

우선 첫 번째로 매개변수와 멤버변수의 성격이 비슷할 때 이름을 동일하게 짓고 this포인터를 통해 구분을 할 수 있습니다.

TempA::tmp(int num)
{
	this->num = num;
}

이런 식으로 멤버변수 num과 매개변수 num을 구분할 수 있습니다.

두 번째로 this포인터로 자기 자신의 참조값을 리턴할 수 있는데 이를 통해서 함수 체이닝이라는 것을 할 수 있습니다.

class	TempA
{
private:
	int num;

public:
	TempA	&one(void)
	{
		this->num++;
		std::cout << this->num << std::endl;
		return (*this);
	}
	TempA	&two(void)
	{
		this->num++;
		std::cout << this->num << std::endl;
		return (*this);
	}
	TempA	&three(void)
	{
		this->num++;
		std::cout << this->num << std::endl;
		return (*this);
	}
};

int	main(void)
{
	TempA a;

	a.one().two().three();
	return (0);
}

출력은 다음과 같습니다.

1
2
3

메인문을 보면 함수가 총 3번 불러진걸 볼 수 있는데
가장 왼쪽 one함수부터 실행되고, 객체 본인의 참조값을 리턴하면 그 뒤에 two함수는 리턴된 객체의 참조값을 통해서 실행되고, 그 다음 three또한 two에서 리턴된 객체를 참조하여 실행합니다.

[ c++ ] namespace
[ c++ ] 클래스, 생성자, 소멸자, 이니셜라이저, this포인터
[ c++ ] c++에서의 const와 static
[ c++ ] 참조자(reference)
[ c++ ] new와 delete
[ c++ ] 함수 오버로딩
[ c++ ] 파일 입출력 (ifstream, ofstream)
[ c++ ] 함수포인터, 멤버 함수포인터
[ c++ ]연산자 오버로딩
[ c++ ] 캡슐화란?
[ c++ ] 상속과 다형성에 대해 알아보자

0개의 댓글