변수를 일일이 선언한 다음에 인자로 다 보내는 건 확실히 불편함이 느껴진다.
그리고 이걸 또 저런 변수들을 가지는 여러 instance들을 만들고 배열로 활용하는 건 너무 불편하다... 관리가 힘들고..
그래서 나온게 구조체이다.
말 그대로 여러 변수들을 하나로 묶은 것이다.
만약 출력하는 함수를 구현한다고 하면 어떻게 해야할까?
인자를 구조체로 통째로 받아서 출력함수를 구현하면 훨씬 편하지.
보통 구조체와 클래스의 차이를 많이 헷갈리는데.
관습상, 구조체는 변수들만 묶을 때 주로 사용하고
클래스는 변수와 '메소드(기능)'을 묶을 때 사용한다.
class의 경우 access specifier, 즉 어떤 변수나 메소드의 접근 여부를 제한할 수 있는데 이건 추후 설명.
public, private, protected 이렇게 3가지가 있다.
그리고, 중요한 개념.
단순히 class를 선언한다 해서 메모리를 먹지 않는다.
class 선언 이후 해당 instance를 선언을 해야 메모리를 할당받는다.
우리가 그냥 int 만 써놓으면 메모리 할당이 안되는데
int a; 하면 메모리 할당이 되는거랑 같은 원리.
구조체 배열을 만드는 방법도 물론 있겠지만..
그것보단 vector을 이용하면 훨씬 편리하겠지. 동적할당의 편리함은 많이 느껴봤으니.
처음 공부하는 순간에 많이 당황스러울 수 있다.
어? 난 class에 분명히 변수들을 선언해놨는데 어째서 main함수에서 접근을 할 수 없지?
...? 어? '접근'?
그렇다. class의 default는 private, 즉 다른 함수에서 접근을 할 수 없게 막아놨다.
아래 사진과 결국엔 같은 모습이다.
그렇다면 궁극적인 의문이 생긴다.
결국엔 클래스에 어떤 변수나 메소드를 선언한다는 이야기는 , 결국엔 다른 함수에서 이를 사용하기 위함인데, 어쩌자는거지?
방법은, 그 클래스 안에 그러한 변수나 메소드를 접근할 수 있는 함수를 선언하는 것이다.
* 클래스 안에서는 private으로 해놓아도, 클래스 안의 함수가 해당 변수나 함수를 접근할 수 있다.
private 자체가 결국엔 다른 함수에서 접근하는 것을 막는 것이지, 같은 클래스 안에서 막는 것은 아니기 때문.
위의 코드의 경우 month, day, year 변수를 접근하기 위해 setDate() 함수를 선언했다.
당연히, 이 함수는 우리가 private 변수에 접근하기 위해 만든 것이고, 즉, 우린 이 함수를 main함수에서 써야하기 때문에
이걸 private안에 넣어놓을 순 없지. public에 넣어서 main 함수에서 이걸 쓸 수 있게 해줘야겠지.
이런 생각이 들 수도 있다. 아니 그냥 모든 변수와 메소드를 public으로 선언하면 안됨? 그냥 그러면 편히 다 접근할 수 있잖아.
맞다. 하지만..?
다 접근할 수 있다라는 표현은 잘 생각해보면, 내가 의도치않았음에도 다른 함수에서 무분별하게 값을 수정할 수 있다는 이야기다. 그러면 당연히 오류가 생기겠지. 그래서 private을 통해 원천봉쇄하는 것이다.
또 하나의 생각. 아까의 생각과 비슷할 수 있는데.
private의 선언된 변수들을 접근하려면 아까 메소드를 따로 선언해야한다했지?
근데 클래스엔 생성자(constructor)란 default 함수가 있다.
클래스와 이름이 같고, 내가 선언을 하든 안하든 저절로 기능한다.
여기서 변수들을 입력받아 private 안의 변수들을 초기화할 수 있다.
생성자가 나오는 순서를 보자.
First 클래스 안에 Second클래스가 있다.
순서상, Second가 먼저 되고, 그 이후 First() 생성자가 나오겠지.
생성자를 활용해 초기화를 할 때, { } 안에서 초기화를 하는 방법도 있지만,
보통은 : 을 활용해 맴버 초기화 목록을 활용한다. { } , () 둘다 활용할 수 있다. 다만 {}가 조금 더 엄격하다 정도만 알자.
다른 인자를 받는 생성자를 여러 개 만들면 과연 좋을까?
사실, 좋지 않다. 어떠한 경우라도, 같은 기능을 하는 함수는 하나만 있는 것이 좋다.
하지만, 그렇다고 예를 들어 id를 default로 하고 이름만 받고 싶은 경우 어떻게 해야할까?
default로 인자를 선언할 때는 오른쪽에서부터 선언을 해야하기 때문에 적절하지 않다.
물론 뭐 인자 순서를 바꿔도 되겠지만, 인자가 많아지면 그건 그것대로 좋은 해결책이 되지는 못한다.
위의 생성자 두 개를 보면 공통되는 부분이 보이지 않는가?
m_name(name_in)은 결국엔 공통이다.
첫번째 방법은, 생성자를 위임받는 것이다.
Student(const string& name_in)
: Student(0, name_in)
{}
Student (const int& id_in, const string& name_in)
: m_id(id_in), m_name(name_in)
{}
이렇게 한다면, 생성자의 기능은 하나만 제대로 선언하면 나머지도 활용할 수 있다.
두 번째 방법은, 그냥 아예 생성자 기능을 하는 함수를 하나 그냥 따로 만들어서 생성자들에 넣어주는 것이다.
만든 init함수가 저렇다.
첫 번째 방법의 코드를 볼 때 많이 의아해할 수 있는데, C++은 절차지향 언어이다.
근데 지금 위의 첫번째 방법의 코드는 순서가 안맞다.
같은 클래스 내에서는, 메서드의 순서는 상관이 없다.
이건 그냥 암기..
생성자와 다른 기능으로, 소멸자(Destructor)라는 것이 있다.
이것 역시 default로 작동한다.
지금 보면, 생성자 0,1 이 만들어지고
없어질 땐 1,0 순으로 없어졌다.
Stack을 생각해보면 이해하기 쉽다.
s2 |
s1 |
지금 s1을 동적할당을 이용해 생성했다.
그리고 먼저 delete을 통해 없어주고 있기 때문에, s1이 먼저 소멸자가 나왔다.
하지만, 소멸자를 직접 호출하는 것은 좋지 않다. 오류가 생길 수 있으므로
생성자만 구현을 했다 해보자.
소멸자가 default로 작동하겠지만,
지금 코드를 보면 무한루프에서 new만 계속 반복될 뿐, delete을 해주는 부분이 없다.
그렇기에 memory leak, 즉 메모리 누수가 발생한다.
이번엔 그래서 소멸자를 따로 구성하면서 delete도 구현해주었다.
그러면 memory leak이 발생하지 않는다.
조금 더 안전함을 원한다면, 소멸자에 if(m_arr != nullptr)을 넣음으로써 안전 장치를 넣어줄 수도 있다.
이번 포스티의 마지막 주제로, this 라는 키워드가 있다.
우리가 Simple이라는 클래스를 만들고, s1 s2라는 각각의 instance를 선언할 때, 얘들이 Simple을 따로따로 만들어서 가지고 있진 않을 것이다.
클래스를 공유하고는 있을텐데 어떻게 구분할까?
바로 this라는 키워드로 작동한다.
this는 해당 instance, 즉 자기 자신의 주소를 반환한다.
개념상으로는 자신의 주소를 넘겨줌으로써 나야, 나 이거쓸게~ 이런 느낌.
this가 포인터(주소)이기 때문에, ->로 접근할 수 있지.
*( ). 로도 되고.
'C++(따라하며 배우는 C++)' 카테고리의 다른 글
Chapter 9-(1). 연산자 오버로딩 (0) | 2021.09.28 |
---|---|
Chapter 8-(2). 객체지향의 기초 (0) | 2021.09.14 |
Chapter 7-(4). 함수 (0) | 2021.09.07 |
chapter 7-(3). 함수 (0) | 2021.09.06 |
chapter 7-(2) . 함수 (0) | 2021.09.04 |