우리는 c에서 구조체를 자주 사용했었습니다.
이 구조체는 클래스의 일종으로써 c에서 구조체를 잘 사용했다면 큰 문제없이 이해할 수 있을거에요.
구조체는 클래스의 일종이라고 했는데 그러면 둘의 차이는 무엇이 있을까요?
struct
로 선언해서 사용했는데 클래스는 class
로 선언해서 사용합니다.class A
{
int num;
void func(void);
};
접근제어 지시자
라는게 있는 데 다음과 같이 세 가지가 존재합니다.멤버함수
에서만 접근이 가능합니다.상속관계
에 있을 때, 유도 클래스
에서 접근이 가능합니다.따로 선언을 해 주지 않는다면 모두 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로 초기화 해라
라는 뜻입니다.
만약 매개변수가 있는 생성자를 이용한다면 괄호 안에 매개변수를 넣어주면 됩니다.
이러한 이니셜라이저를 통한 초기화의 이점은 다음과 같습니다.
이니셜라이저를 통한 초기화와 몸체에서의 초기화를 코드로 비유하면 다음과 같습니다.
int _num = 1; // 이니셜라이저를 통한 초기화
int _num; // 몸체에서 초기화
_num = 1;
그렇기 때문에 선언과 동시에 초기화가 되어야하는 const 상수나 참조자도 멤버변수로 사용할 수 있게 됩니다.
소멸자는 생성자와는 반대로 객체가 소멸
할 때 자동으로 호출되는 녀석입니다.
소멸자는 다음과 같은 특징을 갖고 있습니다.
~
로 시작되고 클래스 이름과 같은 함수명을 갖습니다.~TampA(void)
와 같은 모양을 합니다.반환형
이 없습니다.매개변수
를 받을 수 없기 때문에 오버로딩, 디폴트 값 설정도 할 수 없습니다.이런 소멸자는 함수 몸체에 정의된 내용을 객체가 소멸할 때 자동으로 실행합니다.
보통 소멸자는 생성자에서 할당한 리소스의 소멸에 사용됩니다.
예를 들어
TempA(void)
{
_num = new int;
}
라고 생성자에서 정의되어 있다면
~TempA(void)
{
delete _num;
}
다음과 같이 정의해서 할당된 메모리를 해제합니다.
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++ ] 상속과 다형성에 대해 알아보자