E D R , A S I H C RSS

Cpp�서�멤버함수구현메커니즘


1. 문제 제기를 담� 설명 코드

  • 다ì�Œ 소스ì—�서는 die메소드ì—�서 ìž�ì‹ ì�„ 삭제해ì¤�니다. 그런ë�°. ì‚­ì œë�˜ê³ ë�„ 뻔뻔스럽게 ìž�ì‹ ì�˜ 메소드를 호ìœí•˜ë„¤ìš”. 어떻게 ë�œê²ƒì�¼ê¹Œìš”? 알아봅시다.

~cpp 

#include <iostream>
using namespace std;

class Foo{
public:
	int id;
	Foo(){
		static idSequance = 0;
		id = idSequance++;
		cout << "Create! id = " << id << endl;
	}
	void die(){
		cout << "I suicide. Id is " << id << endl;
		delete this;	// �신 삭제
	}
	void sayMyId(){
		cout << "My Id is no " << id << endl;
	}
	void sayHello(){
		cout << "say Hello" << endl;
	}	
};


int main(int argc, char** argv)
{	
	cout << endl << ":::::: Case 1 - ��할당 " << endl;
	Foo* foo1 = new Foo();// Create! 를 ìœë ¥í•œë‹¤.
	foo1->sayHello();		// Hello ìœë ¥
	foo1->sayMyId();		// id ìœë ¥
	foo1->die();		// �체 삭제
	foo1->sayHello();		// �작 가능
	foo1->sayMyId();		// �작 가능
	

	// id 확�차
	cout << endl << ":::::: Case 2 - id 확�" << endl;
	Foo* foo2 = new Foo();// Create! 를 ìœë ¥í•œë‹¤.
	foo2->sayHello();		// Hello World ìœë ¥
	foo2->sayMyId();		// id ìœë ¥
	foo2->die();			
	foo2->sayHello();
	foo2->sayMyId();		
	
	cout << endl << ":::::: Case 3 - í�¬ì�¸í„° NULL로 하고 메소드 호ìœí•˜ê¸° " << endl;
	Foo* foo3 = NULL;		//  �세한 설명보세요.		
	foo3->sayHello();		// Hello 를 ìœë ¥í•œë‹¤.
	//foo3->sayMyId();		// debug, release 모� �작할 수 없다. 
	//foo3->die();		// debug, release 모� �작할 수 없다.	

	cout << endl << ":::::: Case3 - 지역변수로 선언" << endl;
	Foo foo4;
	foo4.sayHello();		// Hello World ìœë ¥
	foo4.sayMyId();		// id ìœë ¥
	foo4.die();		// debug mode�서 assertion error
	foo4.sayHello();		// release �서 �작 가능
	foo4.sayMyId();		// release �서 �작 가능
	cout << endl;

	return 0;
}
결과는 다ì�Œê³¼ ê°™ì�´ ìœë ¥ë�©ë‹ˆë‹¤. 비êµ�해보면서, ìƒ�ê°�í•´ ë³´ê³  ì�´í•´ê°€ì§€ 않는다면 ìž�세한 설명ì�„ 보세요.
~cpp 
:::::: Case 1 - ��할당
Create! id = 0
say Hello
My Id is no 0
I suicide. Id is 0
say Hello
My Id is no 3604872

:::::: Case 2 - id 확�
Create! id = 1
say Hello
My Id is no 1
I suicide. Id is 1
say Hello
My Id is no 3604872

:::::: Case 3 - í�¬ì�¸í„° NULL로 하고 메소드 호ìœí•˜ê¸°
say Hello

:::::: Case3 - 지역변수로 선언
Create! id = 2
say Hello
My Id is no 2
I suicide. Id is 2
say Hello
My Id is no 2

Press any key to continue

2. ��� 경우� 따른 테스트

2.1. Case1 - ��할당

~cpp 
	Foo* foo1 = new Foo();	// Create! 를 ìœë ¥í•œë‹¤.
	foo1->sayHello();		// Hello World ìœë ¥
	foo1->sayMyId();		// id ìœë ¥
	foo1->die();		// 죽였다.	
	foo1->sayHello();		// 호ìœì�´ ë�œë‹¤?
	foo1->sayMyId();		// �것�..--;

2.2. Case2 - í�¬ì�¸í„° NULL로 하고 메소드 호ìœí•˜ê¸°

~cpp 
	Foo* foo2 = NULL;		// �세한 설명보세요.		
	foo2->sayHello();		// Hello 를 ìœë ¥í•œë‹¤.(ì�Œ--;)
	foo2->sayMyId();		// debug, release 모� �작할 수 없다. 
	foo2->die();		// debug, release 모� �작할 수 없다.	

2.3. Case3 - 지역변수로 선언

~cpp 
	Foo foo3;
	foo3.sayHello();		// Hello World ìœë ¥
	foo3.sayMyId();		// id ìœë ¥
	foo3.die();		// debug mode�서 assertion error
	foo3.sayHello();		// release �서 �작 가능
	foo3.sayMyId();		// release �서 �작 가능

DeleteMe �렇게 보�는 것� 좋지만, 실행할수 있는 완전한 소스 형태를 제시하는 편� � 좋�것 같습니다. --NeoCoin

3. �세한 설명

위� "간단한 설명=코드"를 �용하면서 설명합니다.

C++� 목표는 기존 C� 성능� 해하지 않으면서 OOP를 섞는 것입니다. 필연�으로 OOP� 사고�서 용납하기 어려운 코드를 실행할 수 있습니다. OOP를 C� 구현 위�서 해야 �니다.

� ��하게 보�는 코드를 �해하기 위해서는 C++� �론�� 배경지�� 필요합니다.
  • 첫째로, C++ì�˜ í�´ëž˜ìŠ¤ì™€ ì�¸ìŠ¤í„´ìŠ¤ ìƒ�성ì�„ 알아봅시다.
  • 둘째로, ì�¸ìŠ¤í„´ìŠ¤ì—� ê·€ì†�ë�œ 멤버 함수들ì�„ 실행하는 것ì�„ ìƒ�ê°�í•´ 보겠습니다.
  • 마지막으로, 위ì�˜ 문제제기ì—� 대해 ë¶„ì„�í•´ 봅시다.

3.1. C++�서 �래스� 선언과 �스턴스� �성입니다.

�신� 컴파�러가 �었다고 가정해 봅시다. 우리가 class를 선언하고 컴파�하려면 프로그램� �역� class � Data 들� 저장할 수 있는 "class 틀"� 정보를 담아 놓� 곳� 필요합니다.
~cpp 
class Foo{
public:
    int id;  // <- � 부분� 정보 입니다.
};
C++� Strong typed language �므로 컴파� 시간� 모든 형� 선언�고, 사용�는 것� 검�할 수 있습니다. 하지만 �는 다�과 같� 지역 변수� 국한합니다.
~cpp 
Foo foo4; 
new 키워드로 할당시�는 runtime � class � instance 를 �어 낼수 있어야 합니다. �를 위해 프로그램 안�는 위� id가 int �는 정보를 담는 class� "class 틀" 정보를 담는 곳� 필요합니다.

여기까지가, class 와 struct 키워드가 하는 ë�™ì�¼í•œ 작업입니다. 그리고, class ì—�는 몇가지 ë�” ìƒ�ê°�해야 하는ë�°, 그중 하나가 foo 를 ì�´ìš©í•´ì„œ 어떠한 member 함수를 호ìœí•  수 있는가 입니다.

그러나, 컴파ì�¼ëŸ¬ì�¸ 우리는 ì�´ 정보를 지역 변수ì�´ë“ , new 로 할당ì�´ë“  컴파ì�¼ 시간ì—� ì�¸ìž�ì�˜ typeì�„ ë³´ê³  함수 í˜¸ìœ ìœ íš¨ì„±ì�„ 확ì�¸í•˜ê³ , ì �절한 함수 í�¬ì�¸í„°ë¥¼ 함수 호ìœí•˜ëŠ” 부분ì—� 넣어 줄수 있습니다. 그리고 실행할 수 있는ë�° ì�´ 과정ì�„ ë‘�번째ì—�서 설명합니다.

그외 class와 instance� �성시 vpt와, �� 관계� 대한 pointer 정보가 � 들어 가야 합니다. 그러나 여기�서는 ��하지 않습니다. 둘째로 넘어갑니다

3.2. instance � 귀�� 멤버 함수들� 실행해 봅시다.

ìž� 계ì†� 우리는 컴파ì�¼ëŸ¬ 입니다. 컴파ì�¼ëŸ¬ì�¸ 우리는 member 함수 í˜¸ìœ ë¶€ë¶„ì�„ 함수ì�˜ 실행코드를 가리키는 함수 í�¬ì�¸í„°ë¡œ 모ë‘� êµ�체하였습니다. 그리고 ì�¸ê°„으로 ë�Œì•„옵시다.

C++ 표준안ì—�서 ì „ì—­ì—�서 함수 호ìœê³¼, instanceì—� ê·€ì†�ë�œ 멤버 함수들ì�˜ 호ìœì�„ 가리지 않습니다. 함수 선언과 멤버 함수 ì„ ì–¸ì�˜ 함수 실행 코드는 모ë‘� ë�™ì�¼ 방법으로 ì„ ì–¸ë�˜ê³ , 모ë‘� ë�™ì�¼í•œ 메커니즘ì�˜ 함수 í�¬ì�¸í„°ë¥¼ ì�´ìš©í•´ì„œ 호ìœí•©ë‹ˆë‹¤.

�러한 전제�면, 한가지 �문� ��니다.
전역 함수와 ��한 함수 선언� 형태�면 ��� instance� 어떻게 접근하는가?

무슨 소리� 하면,
~cpp 
class Foo{
public: 
	int id;
	void sayMyId(){
		cout << "My Id is no " << id << endl;
	}
};
�는 함수는 � instance� id �는 ��� 접근합니다.
그러나 ~cpp Foo::sayMyId() 같� 아무런 �� 없는 함수� 실행코드가 함수 선언 �역� 세팅�다면, id �는 ��� 접근할수 없습니다.

C++ �서는 �런 한계를 class � 귀�� 함수들� 처� ��로 해당 class � ��터를 묵시�으로 선언해서 해결하고 있습니다. 즉,
~cpp 
Foo:sayMyId()
�는 함수 실행 코드가 함수 �역� 선언�때 컴파�러가
~cpp 
Foo:sayMyId(Foo* x)
ë�¼ëŠ” 형태ì�˜ 함수로 선언하고, 실행할수 있ë�„ë¡� 만듭니다. 그리고, 호ìœí•œë‹¤ë©´ ~cpp Foo* 부분ì—�
~cpp 
foo1->sayMyId(foo1);
형태로 함수 ��터를 세팅해서 함수 코드를 실행합니다.

C++� �러한 다소 황당한 수로 C와� 컴파�시 호환성� 지키면서 OOP를 구현합니다.

사족. ì�´ëŸ¬í•œ 사연ì�´ classë‚´ì—�서 static 멤버 함수를 선언하고 instanceì—�서 호ìœí• ë•Œ instance ì�˜ 멤버 변수ì—� 접근하지 못하는 ì�´ìœ ê°€ ë�©ë‹ˆë‹¤. static 함수로 ì„ ì–¸ 하면 묵시ì �으로 pointer 를 세팅하지 않고 함수를 호ìœí•©ë‹ˆë‹¤.

�런 배경 지�� 바탕으로 다�으로 넘어갑니다.

3.3. 위� 문제제기� 대해 분�해 봅시다.

다�과 같� 코드를 실행하면,
~cpp 
	Foo* foo1 = new Foo();	// Create! 를 ìœë ¥í•œë‹¤.
	foo1->sayHello();		// Hello ìœë ¥
	foo1->sayMyId();		// id ìœë ¥
	foo1->die();		// �체 삭제
	foo1->sayHello();		// �작 가능
	foo1->sayMyId();		// �작 가능

�렇게 나옵니다.(윈�우즈 플랫��서)

~cpp 
Create! id = 0
say Hello
My Id is no 0
I suicide. Id is 0
say Hello                    // �체 삭제(delete this)후 실행 코드
My Id is no 3604872          // �체 삭제(delete this)후 실행 코드
ì�´ë ‡ê²Œ 나옵니다. (C++ 주ì„� 빼고) 위ì—�서 문제시 ë�˜ëŠ” 부분ì�€, 후반ì�˜ ë‘�가지 ~cpp sayHello() 와 sayMyId() ì�¼ê²�니다. 둘째 설명ì�˜ member 함수를 호ìœí•˜ëŠ” 메커니즘ì�„ ì�´í•´í–ˆë‹¤ë©´
~cpp 
sayHello() -> sayHello(Foo*)
sayMyId() -> sayMyId(Foo*) 
형태로 호ìœë�œë‹¤ëŠ” 것ì�„ ì§�작할 수 있ì�„ ê²�니다. 하지만 ë‘� 함수는 다른 ì �ì�´ 있습니다.

sayHello()는 instance variable� 접근하지 않는다는 것�고, ~cpp sayMyId() 는 접근한다는 ��지요.

따�서, 삭제후� 메모리를 청소(?) 해버리고 난후 해당 instance 부분� 실행해 보리니 id가 다�과 같� 엉뚱하게 나오지요.
~cpp 
say Hello                    �체 삭제(delete this)후 실행 코드
My Id is no 3604872          �체 삭제(delete this)후 실행 코드
instance� 사용�었� 메모리는, 해당 process� 가용 메모리로 �아가지, 접근 금지 �역으로 세팅�지 않습니다. � 부분� delete this 시 해당 instance �역� 값� 어떻게 "청소"하��� 따�서, 플랫� 별로 다르게 나옵니다.
그리고
~cpp 
delete x
�는 코드는 x� 값� 변화시키지 않습니다. 변화시킬수 없습니다. �유는 call by value 로 넘어온 x� 값� NULL로 변경시켜봤�, �향 받지 않는 코드가 경우가 있기 때문�, 변화시킬 필요성� 없습니다.

만약 구현 컴파�러가, 메모리를 system으로 반환하고 접근 금지 �역으로 설정한다면, 다� 강제 접근시 access �러가 나겠지요. �러한 비효율� 구현 플랫� 없� �니다.

foo2~3 �러한 배경 지�� �용해서 결과를 �해할수 있는 보충 설명입니다.

그 중 foo2는 C++�서 볼수 있는 웃긴 예제지요. (foo3� 웃기지만)
~cpp 
	Foo* foo2 = NULL;		//  �세한 설명보세요.		
	foo2->sayHello();		// Hello 를 ìœë ¥í•œë‹¤.
� 코드� 결과는
~cpp 
say Hello
ê³¼ ê°™ì�´ 잘 ë�™ìž‘합니다. í˜¸ìœ í˜•íƒœê°€
~cpp 
Foo::sayHello(foo2)  -> Foo::sayHello(NULL)
� �것�고, sayHello 내�는 pointer 값 NULL � �용한 접근� 없으므로 아무런 �러가 발�하지 않습니다.

OOP�� 사고로 실행할 instance가 없으므로 불가능한 코드지만, 잘 실행�니다. C++�서 함수 실행시 해당 instance� 존재 유무를 검사하지 않습니다. 검사 할 수� 없겠지요. NULL 조차 0 ��는 pointer 값� 해당하니까요.

4. 정리

  • C++ ì—�서 class ì�˜ 멤버 함수를 호ìœí• ë•Œ 멤버 함수ì�˜ 첫ì�¸ìž�를 해당 class ì�˜ instance pointer 로 묵시ì �으로 ì„ ì–¸ë�˜ê³  호ìœë�œë‹¤.
  • 위ì�˜ 호ìœì‹œì—� pointer ì�˜ 유효성ì�€ 확ì�¸í•˜ì§€ 않는다.

5. �붙여 Java�서 비슷한 코드를 봅시다.

instance 유무를 검사하는 Java 코드를 봅시다.
~cpp 
// Main.java file
class Foo{
	public int a;
	void sayHello(){
		System.out.println("Hello");
	}
}
public class Main {
	public static void main(String[] args) {
		Foo foo= null;
		foo.sayHello();
	}
}
ì�´ë¥¼ 실행하면, 다ì�Œê³¼ ê°™ì�€ exceptionì�„ ìœë ¥í•©ë‹ˆë‹¤. ì�´ëŠ” Java Language Specification 2nd (3rdê°€ ì•„ì§� 안나왔군요.) 와 jvm specificationì�„ 참고하세요.
(실행시�� null 값�지 검사하고, 필요시 instance pool�서 instance를 pointer를 �용해서 접근하는 것으로 기억합니다. )
~cpp 
java.lang.NullPointerException
	at Main.main(Main.java:19)
Exception in thread "main" 

C++�서는 NULL � 키워드로 존재하지 않고, 0 ��고 확실히 약��어 있지 않기 때문� 검사�체가 불가능합니다.그러나 vm�� 언어들� 그래서 모� null� 해당하는 키워드가 존재하고 검사가 가능합니다.

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:23:02
Processing time 0.0098 sec