본문 바로가기

C 언어/C++

c++ 기초단계 (11/21)

cout << a;               

void operator << (cout, a);

class operator << (cout, class & a);

                         (ostream &…, a);

 

cout << a << b; 는?

치환을 생각하면 간단하다

cout << a 를 z라 보면

z = operator << (cout, a)

z << b;

operator << (operator << (cout, a), b);

 

 

  2: //#if 1
  3: #include <iostream.h>	//cout
  4: //#else
  5: #include <stdio.h>		//printf
  6: //#endif
  7: 
  8: class array
  9: {
 10: 	private :
 11: 		int *item;
 12: 		int iNum;
 13: 		array()
 14: 		{
 15: 			cout << "디폴트생성자" << endl;
 16: 			item = 0;
 17: 		}
 18: 		array(array & r)
 19: 		{
 20: 			cout << "복사생성자" << endl;
 21: 		}
 22: 		array operator=(array & r)
 23: //		void operator=(array & r)
 24: 		{
 25: 			cout << "대입연산자" << endl;
 26: //			if(this == &r)
 27: //			{
 28: //				return;
 29: //			}
 30: 		}
 31: 	public :
 32: 		array(int iNum)
 33: 		{
 34: 			cout << "인자 1개인 생성자" << endl;
 35: 			this -> iNum = iNum;
 36: 			item = new int(iNum);
 37: 			for(int iCnt=0; iNum > iCnt; ++iCnt)	//동적할당받은 *item의 초기화
 38: 			{
 39: 				item[iCnt] = 0;
 40: 			}
 41: 		}
 42: 		~array()
 43: 		{
 44: 			cout << "소멸자" << endl;
 45: 			delete [] item;		//동적할당 해제
 46: 		}
 47: 
 48: 		void print(ostream * o)	//cout은 ostream의 객체
 49: 		{
 50: 			for(int iCnt=0; iNum > iCnt; ++iCnt)
 51: 			{
 52: 				*o << item[iCnt];		//cout대신 *o
 53: 			}
 54: 			cout << endl;
 55: 		}
 56: 		
 57: };
 58: 
 59: int main()
 60: {
 61: 	cout << "1.-----------------------------------\n";
 62: 	array obj(10);
 63: 	obj.print(&cout);	//print함수 호출(&은 *니 써줌)
 64: 	cout << "2.-----------------------------------\n";
 65: 	
 66: 	return 0;
 67: }
 68: 

 

000호출_00000소스

000호출

 

템플릿(template)

  - 모든형을 다 선언할 수 있는 클래스

3자리2_00000소스  

3자리2

0123456789

3번 자리에 2가 들어간 것을 확인할 수 있다.

 

이번에 다른 자료형을 넣어보자.

3.2

array<float> obj1(10)  //float형 객체 10칸을 할당을 하고

setMember함수를 호출해서 3.1을 넣었다.

print함수를 호출해 출력해보면

함수밖_00001

 

그럼 함수를 밖으로 내보냈을 땐?

함수밖_00000

일단 함수는 클래스 내부에 똑같이 선언해주고

밖에다 class위에 선언했던 템플릿을 똑같이 선언하고

스코프연산자를 쓰고 <>내에 위에 선언한 T를 넣는다

하지만 위에 선언한 클래스위에 템플릿 T와는 다르다.

함수밖

함수위에 쓴 T를 이름을 바꿔 TEST로 바꿔서 컴파일 해도 실행은 잘된다.

함수밖_00001_00000

   

 

cat 원파일에붙일파일명(예 : test.cpp) >> 원파일명 (예 : main.cpp)

하면

   1:  test.cpp
   2:   
   3:  void test()
   4:   
   5:  {
   6:   
   7:      cout << “test” << endl;
   8:   
   9:  }
  10:   

////////////////////////////////////////////////

   1:  main.cpp
   2:   
   3:  #include <iostream.h>
   4:   
   5:   
   6:   
   7:  int main()
   8:   
   9:  {
  10:   
  11:      test();
  12:   
  13:      return 0;
  14:   
  15:  }
  16:   

main.cpp 가 이렇게 바뀐다.

   1:  #include <iostream.h>
   2:   
   3:   
   4:   
   5:  int main()
   6:   
   7:  {
   8:   
   9:      test();
  10:   
  11:      return 0;
  12:   
  13:  }
  14:   
  15:  void test()
  16:   
  17:  {
  18:   
  19:      cout << “test” << endl;
  20:   
  21:  }

 

그래서 일단 18일 작성한 소스코드와 오늘 21일 작성한 소스코드를 cat으로 붙였다.

  1: //1118소스와 1121을 cat으로 붙임
  2: //cat main3 >> main11
  3: //#if 1
  4: #include <iostream.h>	//cout
  5: //#else
  6: #include <stdio.h>		//printf
  7: //#endif
  8: #include <stdlib.h>		//malloc
  9: #include <string.h>		//strlen, strcpy
 10: #include <iomanip.h>
 11: 
 12: class emb
 13: {
 14: 	public :
 15: 		int a;
 16: 		int b;
 17: 		int c;
 18: 		int d;
 19: 		char *e;
 20: 		emb()
 21: 		{
 22: 			a = 50;
 23: 			b = 50;
 24: 			c = 50;
 25: 			d = 50;
 26: //			e = (char*)malloc(sizeof("test"));
 27: 			e = new char;
 28: 			strcpy(e,"test");
 29: //			cout << "emb 생성자 호출" << endl;
 30: 		}
 31: 		emb(emb &kkk)
 32: 		{
 33: 			a = kkk.a;
 34: 			b = kkk.b;
 35: 			c = kkk.c;
 36: 			d = kkk.d;
 37: //			e = (char*)malloc(strlen(kkk.e)+1);	//NULL로 인해 +1
 38: 			e = new char;
 39: 			strcpy(e,"test");
 40: //			cout << "emb 복사생성자 호출" << endl;
 41: 		}
 42: 		~emb()
 43: 		{
 44: //			printf("e가 가리키는 주소값 : %08X\n", e);
 45: //			cout << "e가 가리키는 주소값 : " << hex << e <<endl;
 46: //			free(e);
 47: 			delete e;
 48: //			cout << "emb 소멸자 호출" << endl;
 49: 		}
 50: 
 51: //		void operator = (emb & r)	//연산자 오버로딩 지원
 52: 		emb & operator = (emb & r)	//연산자 오버로딩 지원
 53: 		{
 54: //			cout << "emb 대입연산자 호출" << endl;
 55: 			if(&r == this)	//this 현재객체(자신에게는 대입하지 않는다)
 56: 			{
 57: 				return *this;
 58: 			}
 59: 			//내부변수 일치시키기
 60: 			a = r.a;
 61: 			b = r.b;
 62: 			c = r.c;
 63: 			d = r.d;
 64: //			free(e);	//e 날리고
 65: 			delete e;
 66: //			e = (char*)malloc(strlen(r.e)+1);	//새로 할당받고
 67: 			e = new char;
 68: 			strcpy(e, r.e);	//복사하고
 69: 		
 70: 			return *this;
 71: 		}
 72: 		
 73: 		emb & operator = (int r)	//연산자 오버로딩 지원
 74: 		{
 75: //			cout << "emb int 대입연산자 호출" << endl;
 76: 			//내부변수 일치시키기
 77: 			a = r;
 78: 			b = r;
 79: 			c = r;
 80: 			d = r;
 81: 		}
 82: 
 83: 		void print(ostream * o)
 84: 		{
 85: 			*o << "a = " << a << " ";  
 86: 			*o << "b = " << b << " ";
 87: 			*o << "c = " << c << " ";
 88: 			*o << "d = " << d << endl;
 89: //			*o << "f = " << f << endl;
 90: 		}
 91: };
 92: 
 93: class IT:public emb		//상속
 94: {
 95: 	public :
 96: 		int f;
 97: 		IT()
 98: 		{
 99: 			f = 50;
100: //			cout << "IT 생성자 호출" << endl;
101: 		}
102: 		~IT()
103: 		{
104: //			cout << "IT 소멸자 호출" << endl;
105: 		}
106: 
107: 		IT & operator = (IT & r)
108: 		{
109: //			cout << "IT 대입연산자 호출" << endl;	//하위 대입연산자 출력이 먼저
110: 			emb::operator = (r);		//emb 대입연산자 호출을 위해
111: 			if(&r == this)	//this 현재객체(자신에게는 대입하지 않는다)
112: 			{
113: 				return *this;
114: 			}
115: 			f = r.f;
116: 			return *this;
117: 		}
118: 
119: 		IT & operator = (int r)
120: 		{
121: //			cout << "IT 대입연산자 int 호출" << endl;	//obj1 =1;
122: 			emb::operator = (r);		//emb 대입연산자 호출을 위해
123: 			
124: 			f = r;
125: 			return *this;
126: 		}
127: 
128: 		void print(ostream * o)
129: 		{
130: 			*o << "a = " << a << " ";
131: 			*o << "b = " << b << " ";
132: 			*o << "c = " << c << " ";
133: 			*o << "d = " << d << " ";
134: 			*o << "f = " << f << endl;
135: 		}
136: 
137: 
138: };
139: 
140: void ref(emb &arg)	//(emb arg)주소가 다르다. &붙이면 참조변수로 바뀜
141: {
142: //	cout << "size " << sizeof(arg) << endl;
143: //	cout << arg.a <<endl;
144: //	cout << "arg add " << &arg << endl;
145: }
146: 
147: emb test()
148: {
149: 	cout << "***********************************\n";
150: 	emb obj;
151: 	cout << "***********************************\n";
152: 	return obj;
153: }
154: 
155: ostream & operator<<(ostream & o, emb & r)
156: {
157: //	cout << "연산자 호출됨" << endl;
158: 	r.print(&o);		//제대로 호출됨
159: 	return o;
160: }
161: 
162: ostream & operator<<(ostream & o, IT & r)
163: {
164: //	cout << "연산자 호출됨" << endl;
165: 	r.print(&o);		//제대로 호출됨
166: 	return o;
167: }
168: 
169: ///////////////////////////////////////////////////
170: ///////////////////cat 붙인부분////////////////////
171: ///////////////////////////////////////////////////
172: 
173: template<class T>		//템플릿
174: class array
175: {
176: 	private :
177: 		T *item;	//int 지우고 T
178: 		int iNum;
179: 		array()		//디폴트생성자
180: 		{
181: 		}
182: 		array(array & r)	//복사생성자
183: 		{
184: 		}
185: 		array operator=(array & r)	//대입연산자
186: //		void operator=(array & r)
187: 		{
188: 		}
189: 	public :
190: 		array(int iNum)
191: 		{
192: 			cout << "인자 1개인 생성자" << endl;
193: 			this -> iNum = iNum;
194: 			item = new T[iNum];	//int T로 바꿈
195: 			for(int iCnt=0; iNum > iCnt; ++iCnt)	//동적할당받은 *item의 초기화
196: 			{
197: 				item[iCnt] = 0;
198: 			}
199: 		}
200: 		~array()
201: 		{
202: 			cout << "소멸자" << endl;
203: 			delete [] item;		//동적할당 해제
204: 		}
205: 
206: 		void print(ostream * o);	//cout은 ostream의 객체
207: 			
208: 		void setMember(int iNum, T value)
209: 		{	//내부 iNum이 인자 iNum이 크거나 같으면
210: 			if(this -> iNum <= iNum)	//내부변수가 인덱스를 넘어가면 안된다
211: 			{
212: 				return;
213: 			}
214: 			item[iNum] = value;		//내가 원하는 iNum 자리에 value을 넣어라
215: 		}
216: };
217: 
218: template<class T>
219: ostream & operator<<(ostream & o, array<T> & r)
220: {
221: //	void print(ostream * o);	//cout은 ostream의 객체
222: 	r.print(&o);
223: }
224: 
225: int main()
226: {
227: 	array<emb> obj1(10);
228: 	obj1.print(&cout);
229: //	cout << obj1;
230: 	emb temp;
231: 	cout << obj1;
232: 	temp = 100;
233: 	obj1.setMember(3,temp);
234: 	cout << obj1;
235: 
236: 	return 0;
237: }
238: 
239: template<class T>		//템플릿 위에 T랑 다른 T
240: void array<T>::print(ostream * o)	//외부에 선언하는 방법
241: {
242: 	for(int iCnt=0; iNum > iCnt; ++iCnt)
243: 	{
244: 		*o << item[iCnt] << "   ";		//cout대신 *o
245: 	}
246: 	cout << endl;
247: }
248: 
249: 

 

main 함수내에

오후_00003

emb 클래스를 템플릿을 이용하여 프린트함수에 있는 내용을 출력해보았고

두번째로는 emb temp 객체를 생성해 그것을 출력해보고

세번째로는 생성한 temp객체에 100을 넣고 3번자리에 temp값을 넣고 출력해보았다.

오후 

 

이렇게 템플릿을 쓰면 동적으로 배열을 만들어 준다.

템플릿 클래스를 쓰는 대신 클래스를 완벽하게 구축해 놓아야 할 것을 명심하자.

 

갑자기 나왔던 CM선생님의 문제

class b obj1;

class a *p = &obj1;

참? 거짓?

 

답은 참이었다…

문제

class b 에 obj;을 객체로 만들었지만 b는 class a 도 가지고 있으므로 참이라 할 수 있다.

 

가상함수란

- 부모 클래스에서 오버라이딩 할 함수의 리턴 타입으로 virtual 키워드를 명시해 주면 가상 함수가 되며

  해당 멤버 함수를 호출하면 자식클래스의 멤버 함수가 호출된다.

- 다형성을 프로그래머에게 제공하기 위한 c++의 virtual 키워드를 앞에 붙이고 선언한 함수

  (다형성 : 하나의 참조변수로 여러 타입의 객체를 참조할 수 있는 것 )

 

가상함수의 원리

- 클래스에 가상함수를 선언하게 되면 vtable이 생성된후 가상 함수들의 포인터가 등록된다 ( 클래스당 한개씩의 vtable 생성 )

(vtable 은 가상함수들의 주소를 가지고있는 목록이라 생각하면된다 )

- 자식클래스가 부모클래스의 가상함수를 오버라이딩 하면 자식클래스의 vtable에 오버라이딩한 함수가 매핑된다

- 다형성을 사용하여 자식클래스가 부모클래스로 형변환 되었을 경우 가상함수는 vtable에서 가져오기 때문에 오버라이딩한 함수를 가져오기 때문에 정확한 함수를 호출할수 있다

 

상속관계에 있을때 상위클래스에서 받아온 메소드를 다시 똑같이 만들어줄때 오버라이딩이라 한다.

가상함수를 배우기 위해 오버라이딩을 이용하여 소스코드를 작성하였다.

상속을 받을 때 public을 잊지 말자.

  1: //가상함수, 오버라이딩
  2: #include <iostream.h>
  3: 
  4: class Musician
  5: {
  6: 	public :
  7:         	void Greet()
  8: 		{
  9: 			cout << "안녕하세요. 저는 뮤지션입니다." << endl;
 10: 		}
 11: };
 12: 
 13: class Singer : public Musician
 14: {
 15: 	public :
 16: 		void Greet()
 17: 		{
 18: 			cout << "안녕하세요. 저는 가수입니다." << endl;
 19: 		}
 20: };
 21: 
 22: class Drum : public Musician
 23: {
 24: 	public :
 25: 		void Greet()
 26: 		{
 27: 			cout << "안녕하세요. 저는 드럼입니다." << endl;
 28: 		}
 29: };
 30: 
 31: class Bell : public Musician
 32: {
 33: 	public :
 34: 		void Greet()
 35: 		{
 36: 			cout << "안녕하세요. 저는 벨입니다." << endl;
 37: 		}
 38: };
 39: 
 40: int main()
 41: {
 42: 	Musician M;
 43: 	Singer S;
 44: 	Drum D;
 45: 	Bell B;
 46: 	M.Greet();
 47: 	S.Greet();
 48: 	D.Greet();
 49: 	B.Greet();
 50: 	return 0;
 51: }
 52: 

결과는

오버라이딩

 

이번엔 main()내에 선언한 각 함수를 주석처리하고

 46: 	Musician *p[4] = {&M, &S, &D, &B};
 48: 	for(int iCnt=0; iCnt<4; ++iCnt)
 50: 		(*p[iCnt]).Greet();		//우선순위 높은 () 필수

포인터배열을 써서 위 방법으로 출력해보자

 

  1: //가상함수, 오버라이딩
  2: #include <iostream.h>
  3: 
  4: class Musician		
  5: {
  6: 	public :
  7: 		void Greet()	
  8: 		{
  9: 			cout << "안녕하세요. 저는 뮤지션입니다." << endl;
 10: 		}
 11: };
 12: 
 13: class Singer : public Musician	//상속
 14: {
 15: 	public :
 16: 		void Greet()
 17: 		{
 18: 			cout << "안녕하세요. 저는 가수입니다." << endl;
 19: 		}
 20: };
 21: 
 22: class Drum : public Musician
 23: {
 24: 	public :
 25: 		void Greet()
 26: 		{
 27: 			cout << "안녕하세요. 저는 드럼입니다." << endl;
 28: 		}
 29: };
 30: 
 31: class Bell : public Musician
 32: {
 33: 	public :
 34: 		void Greet()
 35: 		{
 36: 			cout << "안녕하세요. 저는 벨입니다." << endl;
 37: 		}
 38: };
 39: 
 40: int main()
 41: {
 42: 	Musician M;
 43: 	Singer S;
 44: 	Drum D;
 45: 	Bell B;
 46: 	Musician *p[4] = {&M, &S, &D, &B};
 47: 	
 48: 	for(int iCnt=0; iCnt<4; ++iCnt)
 49: 	{
 50: 		(*p[iCnt]).Greet();		//우선순위 높은 () 필수
 51: 	}
 52: 
 53: /*	M.Greet();
 54: 	S.Greet();
 55: 	D.Greet();
 56: 	B.Greet();
 57: */
 58: 	return 0;
 59: }
 60: 

결과는

오버라이딩_00000

이상하게도 뮤지션만 인사를 한다.

Musician의 포인터 배열을 썼기 때문일까…(가르쳐주셈)

여튼 부모 클래스에 있는 함수이름앞에 virtual을 써서 출력을 하게 된다면

오버라이딩

정상적으로 출력된다.

 

주의할 점은 포인터배열을 썼을 때 초기화는 필수이다. 안그럼 컴파일은 되겠지만

실행하면 세그멘테이션 오류라는 글을 볼 수 있다.

 

부족한 점 채워주시고 틀린곳 있음 지적 바랍니다.

'C 언어 > C++' 카테고리의 다른 글

c++ 기초단계 (11/22)  (1) 2011.11.23
c++ 기초단계 (11/18)  (0) 2011.11.18
c++ 기초단계 (11/16)  (0) 2011.11.16
c++ 기초단계 (11/15)  (0) 2011.11.15
c++ 기초단계 (11/14)  (0) 2011.11.14