강의/computer science

[ComputerScience] c++의 Class와 Class Template

하기싫지만어떡해해야지 2024. 10. 6. 19:08

이 게시글은

서울대학교 데이터사이언스대학원

조요한 교수님의

데이터사이언스 응용을 위한 컴퓨팅 강의를

학습을 위해 재구성하였습니다.


이번 시간에는

c++에서 ₩class와

class template에 대해서

배운 내용에 대해서 정의해보려고 한다

 

우선 c++의 개발자

Bjarne Stroustrup의

Why I created C++

영상을 보면

c++을 왜 만들었는지에 대한

이야기가 나오는데

c에는 없는 class의 개념을

도입하기 위해서 만들었다고한다

 

 

Simula라는 언어에서

class의 개념을 가져온 뒤

c의 빠른 속도와 class의 개념을 결합시킨

언어를 만들기 위해서

c++을 개발했다고한다

 

c에서는 structure라는 개념이 있는데

이를 c++에서 object의 개념으로 확장시켰다고한다

 

 

Class

class란 무엇일까

 

아마 객체지향을 따로 배우지않은 사람도

한 번쯤은 들어봤을 것이다

 

나같은 경우는

첫 코딩을 iOS swift로 하는 바람에

코딩 시작부터 강제로(?)

배워버린게 객체지향과

class의 개념이었는데

 

class의 개념을 인터넷에 찾아보거나

gpt한테 물어보면

뭐 청사진, 붕어빵 틀,,

어쩌구 할텐데

조금 구체적으로 class의 정의에 대해서

정리해보려고한다

 

 

class는 내부적으로 2개가 있는데

Attribute와 Method가 있다

 

Attribute는 데이터에 관한 것을 말한다

데이터를 저장하고 관리하는 것을

class내에서 attribute라고 한다

 

Method는 procedural information을

다루는 것을 말하는데

간단하게 function이라고 이해하면 쉽다

data들이 어떻게 기능, 동작, 작동하는지를

정의한 것을 method라고 하고

method, function, behavior이라고도 불린다

 

이렇게 크게 두개의 정보가

class안에 들어있고

이러한 attribute와 method를

정의해놓은 일종의 template이

class이다

 

한마디로

int, string이 이런것들이

standard한 data type이라면

class는

내가 직접 만드는 data type이라고

생각하면 편하다

 

 

보통 class object라고 많이 부르는데

위에서 class는 template이라고 했는데

class object는 무엇일까?

 

그냥 class를 정의만 해준거라면

위에서 말했듯

단순히 blueprint처럼

설계도만 작성해준 것과 같다

 

이 blueprint를 실제 건물(object)라고

부를 수 있을 때는

실제 건물이 만들어졌을 때이다

 

따라서 class object라고

부를 수 있을 때는

실제 runtime에서

class가 메모리에 올라가서

한 개의 instance가 되었을 때

이를 class object라고 부른다

 

각각의 object들은

data attribute나 behaviors(methods)들을

entities(개체들)로 갖고 있다

이러한 object들은

instance라고도 부른다

 

class를 instance라고 부를 때는

class라는 하나의 설계도가

class를 생성하는 초안이 되었다는 것을

강조하고 싶을 때 이렇게 부른다

 

 

위의 why I created c++에서

C의 structure를 object로 만든게

c++이라고 했는데

그렇다면 c의 structure와

c++의 class를 비교해보자

 

위가 C에서의 structure이고

아래가 c++의 클래스이다

 

가장 큰 차이점은

structure에서는 method에 대한

정의는 내릴 수 없지만

class에서는 method에 대한

정의를 내릴 수 있다는 점이다

 

 

 

간단하게 SimpleVector라는

class를 c++에서 구현해보고자한다

 

우선 SimpleVector 안에는

3개의 attribute를 넣어줄거고

이는

int 포인터인 array

int인 size와 capacity이다

 

그 다음 3개의 method도

정의해줄건데

resize(), getSize(), addElement()

함수들을 정의해 줄 것이다

 

 

간단하게 위에서 정의한

attributes과 methods를 이용해서

SimpleVector라는 class를 정의해본 것이다

 

맨 처음

int* array, int size, int capacity를

정의해준다

 

그 다음 밑에

SimpleVector(int initialCapacity)

:size(0), capacity(initialCapacity)

와 같은 구문이 있는데

이는 constructor라고 한다

 

뒤에가서 더 자세히 배울 건데

이 constructor는

class가 instance화 될 때 초기화를 해주는 부분이고

자동으로 실행된다

 

constructor 내부에

array = new int[capacity];

와 같은 구문이 있는데

constructor가 호출되면서

capacity만큼 array가 할당된다

 

이건 나중에 constructor가 나올 때

더욱 자세히 알아보도록 하자

 

그 다음은

~SimpleVector()와 같은

구문이 있는데

이는 Destructor로

class object가 사라질 때 자동으로 실행되고

memory를 release시켜주는 역할을 한다

 

그 뒤에는

element를 추가해주는

addElement함수

 

capacity를 늘려서

새로운 array를 만들어주는

resize함수

 

size를 return하는

getSize함수가 정의되어있다

 

그리고 main함수에서

SimpleVector vec(10);

과 같은 방식으로

SimpleVector라는 class를

instance화해준다

 

 

 

class가 instance화 될 때를 잘 보자

 

SimpleVector vec(10);을 통해서

class를 instance화 해줬다

 

그럼 내부적으로는

vec이라는 class object에 들어간

attribute들이 메모리 공간에

연속적으로 할당된다

 

array는 포인터변수니깐

8바이트

size와 capacity int니깐

4바이트씩

총 16바이트가 stack에

vec을 위한 공간으로 할당된다

 

 

class 내부에 있는

attribute나 method에 접근하려면

어떻게 해야할까

 

같은 class 내부에서는

그냥 단순 이름만으로 호출이 가능하다

위를 보면 resize() 함수를 그냥

호출하고 있는 모습을 볼 수 있다

 

아니면 같은 class 내부에서

this -> capacity와 같이

this를 이용해서도 접근 가능하다

 

this는 파이썬의 self와 비슷한 역할로

class 자기 자신이 저장된 주소를 가리키는

class 포인터이다

 

하지만 외부에서 class 내부의

entities에 접근할 때는

dot operator를 이용해서 접근해야한다

vec.addElement와 같은 방식으로

접근할 수 있다

 

 

Constructor

 

 

아까 위에서 잠깐 언급했던

Constructor에 대해서

자세히 살펴보자

 

constructor는 class에 정의되어있는

특별한 메소드이다

 

class가 instance화가 될 때

자동으로 호출이 되어서

class를 initialize 해준다

 

python의 init()과

비슷한 역할이다

 

constructor의 이름은

무조건 class의 이름과 동일하며

아무것도 반환해선 안된다

 

 

그렇다면 아까 위에서 봤던

Constructor 옆에

: size(0), capacity(initialCapacity)

이 구문은 무슨 역할을 하는지 알아보자

 

이는

initializer list라고 불리는데

class에 있는 attribute들을

초기화 할 때 사용한다

 

constructor 내부에 넣어서

초기화 시켜주는게 아니냐는

생각이 분명 들텐데

constructor 내부에서 초기화를 해줘도되고

initalizer list와 같이 초기화를 해줘도되는데

두 가지 방법은 차이점이 있다

 

constructor 내부에서 초기화를 한다면

class가 declare가 되고 난 뒤

메모리에 class를 위한 공간이 생성이되고

그 후에 초기화가 수행이 되는 것이다

한 마디로, 기본 초기화 후에

추가적으로 수행이 된다는 느낌이다

 

하지만 initailizer list에 넣어서 초기화를 하면

class가 declare가 일어나는 동시에

초기화가 수행이 되는 것이다

capacity가 메모리에 할당이 되는 순간에

initialCapacity의 값을 넣어주는 것이다

선언과 동시에 실행이 된다

 

그렇다면 initilizer list가 왜 유용할까?

 

attribute를 class에 한 번 넣으면

const 형태로 바꾸지 못하게

선언하고싶을 때가 있다

 

위 ppt에서 코드를 보면

value라는 int 변수를

const로 정의했다

 

그런데 이런 value를

constructor 내부에서 초기화를 해주면

compile error가 발생한다

 

왜냐하면 const는 값을 바꿀 수가 없는데

초기화하면서 값을 바꿔주려하고있기 때문이다

 

이러한 경우에는 반드시

initializer list에서 초기화해줘야한다

 

또한 같은 예시에서

string str attribute에

초기화할 때

initStr를 받아서

constructor 내부에서

str를 initStr로 할당해주는데

이렇게 하면

내부적으로는 두 번에 걸쳐서

초기화를 해주는 것과 같다

 

맨처음에 빈 str가 할당되고

initStr를 받아와서 다시

str에 initStr가 할당되는 것이다

 

내부적으로 두번에 걸쳐 초기화해주는 것보단

initilizer list를 이용해서

str 선언과 동시에 initStr을

바로 할당해주는게 좋다

 

 

 

그렇다면 실제로

위 ppt의 코드처럼

vec = {1, 2, 3, 4, 5}와 같이

초기화해주려면

SimpleVector를 어떻게 구현해야할까?

 

SimpleVector의 constructor에

std::initializer_list를 인자로 받아야한다

이는 앞에서 나온 initializer_list와는 다른 개념이다

 

이렇게 해주면

size는 0으로 초기화가 되고

capacity는 elements의 size로 초기화가 된다

 

그다음 array에는 elements의

요소들이 들어가서 array가 초기화된다

 

 

 

object를 만들 때 컴파일러는

프로그래머가 어떤 argument를 넣었는지를

보고 맞는 constructor를 찾아서 호출한다

 

만약에 constructor가 없다면

empty argument를 갖는

constructor가 자동으로 생성돼서

호출되게 된다

 

이렇게 자동으로 생성되는

constructor를

default constructor라고 한다

 

하지만 최소 한 개의 constructor라도

있다면 default constructor는

생성이 되지 않는다

 

 

 

class constructor에 따라

어떻게 class를 초기화하는지

나열한 것이다

 

각 arguments들에 따라

그에 맞는 constructor를 찾아서

컴파일러가 자동으로 수행시킨다

 

 

Destructor

 

 

Destructor도 앞에서

잠깐 언급을 했는데

class 이름 앞에 ~를 붙여준다

 

Destructor가 하는 역할은

class가 할당을 했던 여러가지들을

다시 해제해주는 역할을 한다

 

여러 개의 destructor는

선언이 불가능하다

 

destructor 역시

class가 scope에서 사라질 때

자동으로 호출이 된다

 

class가 stack에 있다면

class object에 대한

모든 call이 끝났을 때

자동으로 호출이 되며

 

class가 heap에 있다면

개발자가 class에 할당된 메모리를

delete하는 순간 자동으로

destructor가 호출이 된다

 

하지만 manual하게도

호출이 가능하다고 한다

 

 

 

 

그렇다면 다른 method들은

어떻게 정의되었는지 확인해보자

 

resize는

기존의 capacity를 2배 늘린 다음

newArray를 만들어서

늘어난 capacity만큼 할당해준다

 

그 다음 newArray에

기존의 array element들을 넣어준다

 

기존 array는 delete해준 뒤

newArray는 resize함수가 끝나면

사라지는 local 변수이기 때문에

array에 newArray를 copy해준다

 

addElement는

size와 capacity가 같다면

새로운 element를 넣을 수 없으므로

만약 size가 같다면 resize를 시켜준 뒤

size의 위치에 새로운 element를 넣어주고

size를 한개 증가시켜준다

 

getSize는

size값을 반환해주는 함수이다

getSize() 뒤에 붙은 const는

해당 함수에서는 attribute를

변경하는 일이 없다고 컴파일러에게

알려주는 역할을 한다

 

그럼 compiler는

컴파일을 할 때

좀더 효율적으로 optimize를 할 수 있다

 

 

Access Specifiers

 

 

access specifier는

외부에서 class 내의

attribute나 method들에

접근 가능 여부를

설정해주는 것이다

 

c++에서는 크게

public, protected, private

3가지가 있다

 

public

memeber들이 프로그램의

어느 영역에 있어도 접근이 가능하다

main함수든 어디든

어디에서나 class 내부에 접근이 가능하다

 

protected

그냥 class 외부에서는

접근이 불가능하고

class 내부 혹은

상속받은 class나 friend class에서만

접근이 가능하다

 

private

가장 강한 보안이다

상속받은 class에서도

접근이 불가능하며

무조건 class 내부에서만 접근이 가능하다

 

안전하게 프로그래밍을 하기 위해선

수정할 일이 없다면

무조건 private으로 해주는게 좋다

그래야 abstraction으로

interface도 더욱 깔끔해지고

원하지않게 attribute가

수정되는 일이 없다

 

 

Class Template

 

그렇다면 지금까지 사용해왔던

SimpleVector class에

한 가지 불편한 점이 있다면 무엇일까

 

이는 바로 포인터 변수 array가

int로 데이터 타입이

고정되어있다는 점이다

 

int 대신에

double이나 string도

해주고싶다면 어떻게 해야할까

 

이전 시간에 공부했던

function template과 비슷하게

class template을 사용하면 된다

 

generic한 type T를

설정해준 뒤

int* array를

T* array로 해주면

int, string, double도

담을 수 있게 된다

 

 

 

class template로 정의된 class는

위와같이 선언해줄 수 있다

 

class 이름 옆에

data type을 정의해주면된다

 

 

 

그렇다면 class template을 이용해

class를 선언해주기 위해서

class 내부도 함께 바꿔보자

 

T* array로 변경해줬으니

이와 관련된 부분은 모두

T로 변경해줘야한다

 

newArray도 T로

addElement할 때

element도 T로 변경해준다

 

 

 

 

class template은 어떻게

instance화 되며

사용하면 어떤 장점이 있을까?

 

template의 arguement가 달라질 때마다

compiler는 그 argument에 특화되게

컴파일을 하고 실행을 한다

 

따라서 Optimize가 가능하고

implementation이 간단해지는

장점 등이 있다

 

 

 

class template을 사용한 예시는 뭐가 있을까

 

우리가 자주 사용하는

vector나 map 모두

class template으로 정의가 되어있다

 

이런걸 Standard Template Library라고해서

STL이라고 부르며

smart pointers도

이러한 방식으로 정의가 되어있다