1. typename


위에서 Line 6의 T::test는 type T에서 test를 꺼내는 것인데,
이때 test는 두 가지로 해석할 수 있다.



1) static 변수를 꺼내는 경우



2) class 내의 type을 꺼내는 경우 (typedef 또는 class 내에 정의된 클래스)

1), 2) 처럼 실제 type인 경우에는 컴파일러가 둘을 구분하는데에 문제가 발생하지 않는다.
(만일 같은 이름의 static 변수와 타입이 있을 경우 이미 정의된 것을 재정의 한다고 컴파일 에러가 발생)

하지만 맨 첫번째 경우처럼 template argument 인 경우 컴파일 time에 둘을 결정할 수 없다.



이를 막기 위해 T::type 앞에 typename을 붙여서 컴파일러에게 typename이란 것을 명시해야 정상 동작한다.



Line 5처럼 typename을 앞에 붙이면 T::type을 type으로 여겨서 뒤의 *를 포인터로 해석하고, 정상적으로 동작한다.



2. template


class 안에 template이 있는 경우를 생각해 보자.

(템플릿 멤버함수 또는 클래스 안의 템플릿 클래스)




이 경우 Line8은 컴파일러가 CCC 내부의 print가 template인 것을 확인하여 <를 해석할 수 있지만,


Line 9는 T::print가 static variable인지 구분 되지 않고,

<를 대소 연산자로 확인하여야 하는지 구분이 되지 않아 에러가 발생한다.


이를 막기 위해서는 T::template print<int>(x) 로 수정하여 print가 template 임을 명시하여야 한다.


만일 template 함수가 아니고 template class인 경우는, 1의 typename 까지 같이 붙여줘야 한다.




Line 11은 typename 과 template 둘 다 있어야만 에러가 발생하지 않는다.




3. value_type


임의의 container (vector, list 등) 에서 안에 들어 있는 값을 꺼내야 할 경우, type은 어떻게 해야 할까?



예시로 container 가 list<int> 로 들어온다면 ? 에 int를 넣으면 되겠지만, list<double> 이라면 ?에 double이 들어가야 한다.

즉, template에 따라서 x의 type이 바뀌는데 이는 T를 통해 유추할 수 없다.


이를 해결하려면, template 인자를 두 개를 (list<int>, int) 받는 방법이 있다.

(아니면 template template parameter로 list를 받고, int를 받는 방법이 있다.)



foo1, foo2 둘 다 가독성이 떨어진다.


이때 list, vector 안에서 typedef로 안의 값의 타입을 보관하고 있다면, foo 안에서 쉽게 꺼낼 수 있다.



이때 Line 10에서 꺼낼 경우 2번에서 언급한 typename 을 사용해야지 컴파일 에러 없이 type을 꺼낼 수 있다.


STL은 이런 값들이 많이 정의되어 있는데, 정리하면 아래와 같다.

침고: std::vector 링크


value_type

T

 allocator_type

Allocator

size_type

unsigned int, size()의 return type

difference_type

(signed) int, 두 pointer를 뺐을 경우의 type

 reference

 T&

 const_reference

 const T&

 pointer

 Allocator::pointer

 const_pointer

 Allocator::const_pointer

 iterator

 임의 접근 반복자

 const_iterator

 상수형 임의 접근 반복자

 reverse_iterator

 reverse_iterator (맨 뒤 원소에서 시작해서 앞으로)

 const_reverse_iterator

 const reverse iterator


'프로그래밍 > C++' 카테고리의 다른 글

템플릿 주의할 점  (0) 2018.12.24
템플릿 (Template)  (0) 2018.11.04
함수 바인딩  (0) 2018.10.29
가상 함수 (Virtual Function)  (0) 2018.10.23
상속 (Inheritance)  (0) 2018.10.22

1. template은 function이나 class가 아니다.


이걸 헷갈리면 function 또는 class를 넣어야 할 곳에 template을 넣어서 에러가 발생하게 된다.



만일 템플릿 인자로 템플릿을 넣어야 할 때는 템플릿 템플릿 파라미터를 써야 한다.




2. 선언부와 구현부를 분리할 때



2-1. 선언부

전체적으로, class 정의 내에서는 <T>를 명시해주지 않아도 된다.

- Line 11, 35: 디폴트 생성자

템플릿 인자의 기본값은 x = 0 으로 해도 되지만 x = T() 처럼 디폴트 생성자를 호출해주는 것이 더 좋다.

알다시피 기본 인자는 선언부에만 적고 구현부에 쓰지 않는다.


- Line 12, 37: 복사 생성자

복사 생성자에는 Test<T>& 대신 Test& t로 해도 되지만, 전자가 더 명확하다.


- Line 19: ostream& operator << 와 friend의 문제. (참고링크)

1. Extrovert: template 으로 operator<<를 friend 선언하고, 외부에 구현

이때 문제는 서로 다를 수 있는 Test<T>, Test<U>에 대해서 operator<<가 다 friend 등록되어 버린다는 점에 있다.

(Line 20의 template<typename U>를 제거하고 Test<T>로 변경하면 warning 발생

2. Introvert: 선언 후 바로 구현

이렇게 하면 다른 Test<U>에 대해서는 friend 되지 않는다.

이 경우는 선언-구현을 나눌 수 없다.

3. Taking advantage of extrovert

미리 외부에 operator<<를 template function으로 선언해 놓으면 1번과 달리 friend operator<<가 template이 아니고, Line 27처럼 class template argument T를 그냥 갖다 쓸 수 있다.

이 경우 구현부는 1번과 동일하게 가져가도 된다.


2-2. 구현부


* 템플릿은 선언부와 구현부를 헤더파일과 소스파일로 분리할 수는 없다.


- 모든 메소드마다 Test<T>를 붙여야 해서 template <typename T>도 다 붙여줘야 한다.


- Static 변수

Line 29의 static 변수는 클래스 내부에 선언하였으면 Line 58처럼 클래스 외부에서도 한번 초기화하여야 한다.




3. 템플릿 멤버 함수 (Template member function)


Line 30은 template member function의 선언이고, line 60은 구현이다.

여기서 Line 61을 따로 쓰지 않고 Line 60에 template <typename T, typename U> 로 한번에 쓰면 에러가 발생한다.


3-1. 일반화된 복사 생성자


현재 복사 생성자는 Line 12에 선언된 대로 같은 Test<T>에 대해서만 선언되어있다.

이를 테면 Test<int>와 Test<short>는 다른 타입이기 때문에 복사 생성 시 에러가 발생한다.

Test <short> r = p; // error

이를 막으려면 서로 다른 Test<T>, Test<U> 간에 복사 생성자를 추가해줘야 한다.

이는 템플릿 멤버 함수로 구현할 수 있다.

이때, Test<T>와 Test<U>는 서로 다른 클래스이므로 friend로 등록해주어야 한다.



이렇게 복사 생성자가 제공되면 ==, !=도 제공해주어야 한다.


shared_ptr 등의 스마트 포인터를 구현하는 경우 반드시 일반화된 복사 생성자가 있어야 한다.

(파생 클래스를 shared_ptr 로 받는 경우 서로 다른 클래스를 복사 생성할 수 있어야 한다.)


'프로그래밍 > C++' 카테고리의 다른 글

typename명령어, template 명령어  (0) 2018.12.26
템플릿 (Template)  (0) 2018.11.04
함수 바인딩  (0) 2018.10.29
가상 함수 (Virtual Function)  (0) 2018.10.23
상속 (Inheritance)  (0) 2018.10.22

1. 함수 오버로딩으로 일관된 형태의 함수 만들기

 

C++은 인자를 다르게 하여 같은 이름의 함수를 여러개 만들 수 있다.

 

이를 이용하여 인자 타입에 따라 같은 동작을 하는 라이브러리를 만들 수 있다.

(그렇지 않고 한 타입에 대해서만 정의한다면, 다른 타입에서 사용할 수 없거나 강제로 형변환 되어버린다)

 

 

만일 double square(double x)가 없다면, square(3.3)은 3.3이 int로 형변환 된 후 int로 반환되어버린다.

 

이렇게 동일한 코드를 반복적으로 정의하기 위해서는 C의 매크로를 쓰거나 C++의 템플릿을 쓸 수 있다.

 

 

 

2-1. 매크로

 

매크로를 이용하면 전처리 시간에 코드를 생성할 수 있다.

 

위의 동작을 아래와 같이 매크로로 처리하면 한줄로 타입에 대해 정의할 수 있다.

 


단점은 매번 함수를 사용하기 전에 자기가 원하는 타입으로 선언했는지 생각해보고,

 

사용 안 했으면 먼저 MAKE_SQUARE()를 선언해줘야 한다는 것이다.


그리고 두번 같은 선언을 하면 에러가 발생한다.

 

2-2. 템플릿

 

C++에서 추가된 템플릿을 사용하면 컴파일 시간에 코드를 생성할 수 있다.

 


이렇게 코드를 추가하면 square 함수를 사용할 때마다 컴파일 시간에 함수를 추가한다.

(이미 추가되어 있다면 추가된 함수를 사용한다.)

 

즉 템플릿 구문 만으로는 함수가 아니고, 만일 사용하지 않았다면 어셈블리 코드에 함수가 하나도 추가되지 않는다.


여기에는 square 에 대한 내용이 없다.


여기에는 Line 44에 square<int>, Line 64에 square<double>가 생성된 것이 보인다.


이를 지연된 인스턴스화 (Lazy Instantiation)라 부른다.




3. 지연된 인스턴스화 (Lazy Instantiation)


클래스 템플릿 중 사용되지 않은 멤버 함수는 인스턴스화 되지 않는다.



Line 73의 _ZN4TestIiE3addEii(Test<int>::add), Line 97의 _ZN4TestIiE3subEii(Test<int>::sub), Line 120의 _ZN4TestIdE3subEdd(Test<double>::sub) 세 개는 인스턴스화 되었는데, Test<double>::add는 되지 않았다.


같은 원리로 설사 코드에 문제가 있더라도 사용되지 않았다면 컴파일러가 생성하지 않기 때문에 컴파일이 잘 된다.

(클래스 템플릿이 아닌 경우는 사용되지 않더라도 컴파일 에러 발생)



위의 코드에서 Test1은 에러가 있는 멤버 함수 erroneous가 있더라도 컴파일러에 의해 에러가 발생하지 않는 반면, Test2는 사용된 적도 없지만 컴파일 에러가 발생한다.


'프로그래밍 > C++' 카테고리의 다른 글

typename명령어, template 명령어  (0) 2018.12.26
템플릿 주의할 점  (0) 2018.12.24
함수 바인딩  (0) 2018.10.29
가상 함수 (Virtual Function)  (0) 2018.10.23
상속 (Inheritance)  (0) 2018.10.22

1. 함수 바인딩 (Function binding)


함수 호출하는 코드에서 실제 호출할 함수를 결정하는 것을 함수 바인딩이라 한다.



L15 ~ 17 은 다 같은 객체 s의 멤버 함수지만 호출되는 함수는 다르다.


함수 바인딩에는 static binding과 dynamic binding이 있다.


Static binding

컴파일 시점에 함수를 결정한다. C++의 비 가상 함수


Dynamic binding

런타임 시점에 함수를 결정한다. C++의 가상함수, JAVA, objective-C 등의 다른 언어에서 쓰인다.




2. Static binding


Static binding은 멤버 함수를 컴파일 시간에 결정해야 하는데, 컴파일 시점에 항상 객체의 타입을 결정할 수는 없다.



L9에서 p는 사용자 입력에 따라 타입이 달라지기 때문에 p가 어떤 타입인지 컴파일러는 알 수 없다.


하지만 print 멤버가 가상함수가 아닌 경우는 static binding 때문에 컴파일 시간에 print 함수를 결정해야 한다.


이때 컴파일러는 p의 포인터 타입만 알고 있기 때문에, p의 타입을 보고 (person* 타입) person의 멤버 함수를 부르게 된다.

(p는 person*이기 때문에 p가 person의 파생 클래스여도 person의 멤버함수 print는 항상 있디)




3. Dynamic binding


Dynamic binding은 가상 함수를 런타임 시간에 결정하는데, 이를 위해 가상 함수 테이블 (virtual function table)을 따로 유지하고, 거기에 가상 함수의 함수 포인터를 보관한다.


다음 예를 통해서 가상함수 테이블의 크기를 유추해 볼 수 있다.



personWOvt는 가상함수가 없기 때문에 사이즈가 1이고 (0인 경우 sizeof가 들어간 다른 구문에 (나누기 같은 경우) 에러를 발생시킬 수 있어 최소 1을 리턴)


person은 가상함수가 있기 때문에 사이즈가 8이다. (32비트의 경우 4바이트씩 증가)


이 가상함수 테이블은 만일 파생클래스에서 가상함수를 오버라이딩 하는 경우, 컴파일러가 부모 클래스의 가상함수를 교체하게 된다.


가상함수의 단점

가상함수마다 메모리가 추가로 필요하다

함수 호출 시 가상함수 테이블을 참조한 후에 호출하여 오버헤드가 발생한다.

인라인 치환을 사용할 수 없다. (컴파일 시점에 치환을 결정할 수 없음)




4. RTTI (Run-Time Type Information)


가상함수 테이블에는 가상함수의 함수 포인터 외에 추가로 이 객체가 원래 어떤 타입인지를 가리키는 정보를 추가로 가지게 된다. 이것을 조사하는 것을 RTTI라고 부른다.


이 타입 정보는 아래와 같이 사용할 수 있다.



1) const type_info reference type으로 받는다.

2) typeid(객체) 또는 typeid(student)로 조사한다.

3) #include <typeinfo>




5. Dynamic_cast


앞에서 나왔던 dynamic_cast가 이 RTTI를 이용한다. 


런타임에 타입을 조사하여 변환 가능하면 포인터를 리턴하고, 

그렇지 않다면 (상속 관계가 없다면) 0을 리턴한다.



student 는 person의 자식 클래스이므로 정상적으로 캐스팅 되지만,

cat은 person의 자식 클래스가 아니기 때문에 캐스팅 되지 않고 nullptr가 리턴된다.

(만일 static_cast를 썼다면, cat도 강제로 캐스팅 되어버린다)

'프로그래밍 > C++' 카테고리의 다른 글

템플릿 주의할 점  (0) 2018.12.24
템플릿 (Template)  (0) 2018.11.04
가상 함수 (Virtual Function)  (0) 2018.10.23
상속 (Inheritance)  (0) 2018.10.22
연산자 재정의 (Operator overloading)  (0) 2018.07.01

1. Upcasting과 Downcasting


일반적인 경우, 포인터 변수에 다른 type의 포인터를 담을 수 없다.

(예외: void* 변수는 다른 type의 포인터를 담을 수 있다.)



하지만 "부모 클래스"의 포인터에 "자식 클래스"를 담을 경우, 자식 클래스는 부모 클래스를 물려받았기 때문에 메모리 앞부분이 부모 클래스와 똑같다. 따라서 포인터 변수로 담을 수 있으며, 이를 upcasting이라 한다.



이때 부모 클래스와 자식 클래스에 같은 이름의 멤버가 있다면, 원래 변수가 자식 클래스였어도 upcasting 된 변수를 통해 접근할 때는 무조건 부모 클래스의 멤버가 불리게 된다.


부모 클래스에서 자식 클래스의 멤버를 부르고 싶으면, 자식클래스로 형변환을 한 후에 불러주면 된다. 이를 downcasting 이라 부른다.


Downcasting을 static_cast<자식클래스>(변수) 로 하게 되면 실행 시간에 변수가 원래 자식 클래스인지, 아니면 부모 클래스인지 확인이 되지 않기 때문에, 정의되지 않은 동작을 할 수 있다. (실제 자식클래스가 아닌데 자식 클래스의 멤버를 부른다)


Downcasting을 dynamic_cast<자식클래스>(변수)로 해줘야 실행 시간에 변수의 type을 체크해서 변수가 원래 자식 클래스였으면 downcasting이 성공하고, 그렇지 않다면 실패하여 null을 반환시킨다. 따라서 dynamic_cast를 사용하는 것이 더 안전하다.

단 그냥 dynamic_cast를 할 경우 "source type is not polymorphic" 에러가 발생하는데, 이를 막기 위해선 person의 소멸자를 virtual로 지정해주어야 한다.




2. 함수 오버라이드 (function override)


위의 경우에서 downcasting 없이 자식클래스의 함수를 부르고 싶으면, 부모 클래스에서 멤버 함수를 가상 함수 (virtual function)으로 정의하면 된다.


이 경우 부모 클래스에서 함수를 선언할 때 virtual 키워드를 앞에 붙인다. (구현부에는 붙이지 않음)


그리고 자식 클래스에서 재정의 할 경우에는 virtual 키워드를 붙여도 되고 붙이지 않아도 된다. (가독성 때문에 붙이는 것이 좋다.)



3. 추상 클래스 (Abstract class)


가상 함수에 정의 대신 = 0; 으로 구현하지 않은 경우를 순수 가상함수라고 하고, 순수 가상 함수를 가진 클래스를 추상 클래스라고 한다.


추상 클래스는 순수 가상함수 때문에 객체를 만들 수 없으며, 오직 파생클래스에서 순수가상함수를 구현한 경우에만 객체를 만들 수 있다.



4. 가상 소멸자


아래와 같이 부모 클래스의 포인터로 자식 클래스를 생성하고나서 반환하는 경우를 생각해 보자.



이때 Line 14에서 p가 소멸될 때 p는 person의 pointer이므로, ~student()를 호출하는 것이 아닌 ~person()을 호출하게 되고, student의 자원에 대해 소멸자가 불리지 않게 된다.


이 문제를 해결하기 위해서는 ~person()을 가상 함수로 선언해 주어서, 자식 클래스의 소멸자가 불리게 해야 한다. (그리고 나서 부모 클래스의 소멸자도 불리게 된다.)

이 경우 자식 클래스의 소멸자는 명시하지 않아도 가상 함수가 된다.



따라서, 클래스를 상속할 경우 반드시 소멸자를 가상함수로 만들어야 한다.

'프로그래밍 > C++' 카테고리의 다른 글

템플릿 (Template)  (0) 2018.11.04
함수 바인딩  (0) 2018.10.29
상속 (Inheritance)  (0) 2018.10.22
연산자 재정의 (Operator overloading)  (0) 2018.07.01
this 포인터  (0) 2017.11.13

0. 개요


클래스간에 상속을 받게 되면, 앞의 클래스의 멤버 변수와 멤버 함수를 물려받게 된다.


이때 상속되는 클래스를 부모 클래스 (Parent Class), 기반 클래스 (Base Class), 또는 상위 클래스 (Super Class)

상속받는 클래스를 자식 클래스 (Child Class), 파생 클래스 (Derived Class), 또는 하위 클래스 (Sub Class) 라 부른다.


클래스간의 관계를 그릴 때는, 부모 클래스를 자식 클래스가 화살표로 가리키는 것으로 나타낸다.

부모 <- 자식


1. 사용법


class A : public/protected/private B {};

A가 B를 상속하고 싶을 때 위와 같이 작성한다.


예를 들어 Student class가 Person class를 상속하는 경우는 아래와 같다.



이 경우 Person <- Student 로 나타낸다.


위에서 B 앞의 public, protected, private 상속은 아래와 같은 성질을 가진다.


1-1. public

부모 클래스의 멤버 변수, 멤버 함수들의 성질은 그대로이다.


1-2. protected

부모 클래스의 멤버 변수, 멤버 함수들 중 public 은 protected로 바뀐다.

즉, 부모 클래스의 모든 변수, 함수는 외부에서 접근 불가능해진다.


1-3. private

부모 클래스의 멤버 변수, 멤버 함수가 private으로 바뀐다.

즉, 부모 클래스의 모든 변수, 함수는 외부 및 자식 클래스에서 접근 불가능해진다.


2. 생성자, 소멸자

생성자는 부모 클래스 생성자-자식 클래스 생성자 순으로 불리고, 

소멸자는 반대로 자식 클래스 소멸자-부모 클래스 소멸자 순으로 불린다.


예를 들어, 위의 person과 student를 아래와 같이 바꾸면 결과는 아래와 같다.



실행결과


이는 자식 클래스의 생성자에, 컴파일러가 자동으로 디폴트 생성자를 넣어준 것이다.

아래 결과를 보면, student는 grade 생성자가 불려도 person은 디폴트 생성자가 불린다.



실행결과


이때 다른 생성자를 불러주려면, 멤버 변수 초기화 하듯이 부모 클래스의 생성자를 불러주면 된다.

(부모클래스 생성자부터 불러야 reorder warning이 뜨지 않는다.)



실행결과


부모 클래스에 디폴트 생성자가 없는 경우, 컴파일러가 디폴트 생성자를 부를 수 없으므로, 이렇게 부모 클래스의 생성자를 명시적으로 불러줘야지만 에러가 발생하지 않는다.



'프로그래밍 > C++' 카테고리의 다른 글

함수 바인딩  (0) 2018.10.29
가상 함수 (Virtual Function)  (0) 2018.10.23
연산자 재정의 (Operator overloading)  (0) 2018.07.01
this 포인터  (0) 2017.11.13
상수 멤버 함수 (const member function)  (0) 2017.11.13

1. 개념


연산자는 기본 타입에 대하여는 정의되어 있지만, 내가 정의한 클래스에 대해서는 정의되어 있지 않다.



연산자 재정의를 하면 내가 정의한 클래스에서도 연산자를 쓸 수 있게 된다.


C++에서는 아래의 연산자를 재정의 할 수 있다.

+, -, *, /, %, ^, &, |, ~, !, =, <, >, +=, -=, *=, /=, %=, |=, <<, >>, >>=, <<=, ==, != <=, >=, &&, ||, ++, --, ,, ->*, ->, ( ), [ ]

C++ 20 추가: <=>



이 경우 Line 12은 x3 = x1.operator+(x2);로 동작한다.



2. 구현



연산자 오버로딩은 멤버 함수로 하는 방법과 일반 함수로 하는 두 가지 방법이 있다.



이때 일반 함수로 하는 경우는 인자가 하나 추가되고, (멤버함수인 경우 첫번째 인자는 *this가 된다)

private member variable에 접근하기 위해 friend 구문을 추가해야 한다.


둘 중에 뭐가 더 좋은지에 대해서는 찾아보면 정답은 없는 것 같다.


전에 들은 강의에서는 멤버 변수가 변하는 경우 멤버 함수로, 멤버 변수가 변하지 않으면 일반 함수로 한다는 이야기가 있었다.


링크에서는 질문자가 "일반 함수가 더 선호되는 이유"를 묻고 있다. 이때 답변이 지적하는 부분은 


"일반 함수로 하는 경우, 서로 다른 자료형에 대해 재정의 가능하다" 이다.


예를 들어, 위의 Point에 대해서 (숫자 * Point)를 Point의 x, y에 숫자 만큼 곱하는 걸로 정의하고 싶을 경우, 멤버 함수로는 구현이 불가능하지만 일반 함수로는 아래와 같이 구현 가능하다.



전위연산자와 후위연산자


전위연산자와 후위연산자 둘 다 operator++ 으로 이름이 똑같은데, 둘을 구분하기 위해 후위연산자는 operator++(int) 로 써야 한다. (이때 int는 구분을 위한 기능만 있을 뿐 실제 인자는 아니다.)


즉, 전위연산자는 T& operator++();

후위연산자는 T& operator++(int);



이때, 후위연산자 정의에 전위연산자를 사용하면, 증가연산자를 바꿔야 할 때, 전위연산자만 바꾸면 후위연산자도 같이 바뀌게 되므로 유지보수성을 좋게 할 수 있다.


3. 주의


::, . (멤버 변수/함수 접근), .*, ? 연산자는 오버로딩 할 수 없다.


=, (), [], -> 는 멤버 함수로만 구현할 수 있다.



참조

https://en.cppreference.com/w/cpp/language/operators

https://stackoverflow.com/questions/4622330/operator-overloading-member-function-vs-non-member-function

http://lab.gamecodi.com/board/zboard.php?id=GAMECODILAB_Lecture_series&no=52



'프로그래밍 > C++' 카테고리의 다른 글

가상 함수 (Virtual Function)  (0) 2018.10.23
상속 (Inheritance)  (0) 2018.10.22
this 포인터  (0) 2017.11.13
상수 멤버 함수 (const member function)  (0) 2017.11.13
정적 멤버 함수 (Static member function)  (0) 2017.11.13

Class 내부에서 this 포인터는 현재 객체를 가리키는 포인터이다.



this->멤버 변수명 을 통해 멤버 변수를 가리키나, 일반적으로 this->는 생략할 수 있다.


Line 6, 7: 멤버 변수명과 인자 이름이 같은 경우는 this->변수명 으로 접근해야 모호하지 않다.


Line 20: this를 리턴하거나 *this를 리턴하여 함수를 연속적으로 실행할 수 있다.


C++에서 클래스의 멤버 함수들을 C에서 구현한다면,

C의 struct에 데이터를 넣고 함수들은 모두 첫번째 인자로 this 포인터를 받는 것으로 생각할 수 있다.


이 경우 private, protected, public 구분이 안 되는 것 말고는 비슷하게 쓸 수 있다.



+ Recent posts