C++ 클래스, 접근속성, scope 연산자(::), inline
- 구조체 안에 함수를 사용할 수 있는 것의 발전되어 클래스가 되었다.
→ C++은 일반 구조체에서도 함수를 사용할 수 있다.(C에서는 불가능)
- 객체는 데이터 위주(예제의 OBJ를 함수로 둘러싼다.)
→ 구조적 프로그램은 자료와 함수가 분리되어 있고, 자료에 쉽게 접근하게 된다.(자료에 대한 보호가 부실)
→ 객체는 자료에 대해 일괄적인 인터페이스를 제공하고 사용용도를 유도할 수 있다.
→ 예제
#include<stdlib.h>
struct emb
{
int *item;
int iNum;
// 구조체 초기화 함수
bool embInit(emb *stVal)
{
// 분홍색 값은 리터럴(문자상수)이다.
if(0 == stVal) // Null 을 입력받으면
{
return false;
}
stVal->item = 0;
stVal->iNum = 0;
cout << "embInit" << endl;
return true;
}
// 구조체 메모리 할당
bool embInit(emb *stVal, unsigned int uiNum)
{
// 분홍색 값은 리터럴(문자상수)이다.
if(0 == stVal) // Null 을 입력받으면
{
return false;
}
else if(0 == uiNum)
{
return false;
}
// item에 동적 할당
stVal->item = (int *)malloc(sizeof(int)*uiNum);
// 동적 할당을 못 받았을 경우
if(0 == stVal->item)
{
return false;
}
stVal->iNum = uiNum;
cout << "embInit" << endl;
return true;
}
// 구조체 메모리 초기화 함수
void embFree(emb *stVal)
{
if(0 == stVal)
{
return ;
}
if(0 == stVal->item)
{
return ;
}
free(stVal->item);
stVal->item = 0;
stVal->iNum = 0;
cout << "embFree" << endl;
return ;
}
};
int main()
{
emb OBJ;
// 자료의 추상화
// OBJ에 함수만이 접근하고 main()은 접근하지 않는다.
// 즉, 함수를 통해서 간접적으로 건드리게 하는데
// 이것을 OBJ가 추상화 되었다. 자료가 추상화 되었다고 한다.
OBJ.embInit(&OBJ); // 0으로 초기화 하는 함수
OBJ.embInit(&OBJ, 300); // 동적할당 및 전체 계수(1200byte)
OBJ.embFree(&OBJ);
return 0;
}
→ 실행 결과
→ 위와 같이 구조체에 함수를 추가하여도 잘 동작 하는 것을 확인할 수 있다.
→ 위에서 함수는 구조체 내에 있으므로 인자로 받을 필요 없이 구조체 내의 변수를 사용할 수 있으므로 인자를 제거한다.
→ 소스를 바꾸어 보면
#include<stdlib.h>
struct emb
{
int *item;
int iNum;
// 구조체 초기화 함수
bool embInit()
{
// 분홍색 값은 리터럴(문자상수)이다.
item = 0;
iNum = 0;
cout << "embInit" << endl;
return true;
}
// 구조체 메모리 할당
bool embInit(unsigned int uiNum)
{
if(0 == uiNum)
{
return false;
}
// item에 동적 할당
item = (int *)malloc(sizeof(int)*uiNum);
// 동적 할당을 못 받았을 경우
iNum = uiNum;
cout << "embInit" << endl;
return true;
}
// 구조체 메모리 초기화 함수
void embFree()
{
free(item);
item = 0;
iNum = 0;
cout << "embFree" << endl;
return ;
}
};
int main()
{
emb OBJ;
// 자료의 추상화
// OBJ에 함수만이 접근하고 main()은 접근하지 않는다.
// 즉, 함수를 통해서 간접적으로 건드리게 하는데
// 이것을 OBJ가 추상화 되었다. 자료가 추상화 되었다고 한다.
OBJ.embInit(); // 0으로 초기화 하는 함수
OBJ.embInit(300); // 동적할당 및 전체 계수(1200byte)
OBJ.embFree();
return 0;
}
→ 실행 결과
→ 인자로써 변수가 필요없어 불필요해진 코드들을 삭제함으로 써 소스의 양이 줄었다.
→ 오버로딩을 객체지향에서는 다향성이라고 한다.
→ 여기에 기능을 추가해서 클래스를 만든다.
→ 구조체는 메모리에 없다. 위의 소스에서 메모리에 올라간 것은 OBJ
→ 예를 들어 붕어빵과 틀을 생각해 보면 틀은 클래스 붕어빵은 객체(OBJ)이다.
→ 클래스의 관점에서는 붕어빵은 같고 객체의 관점에서는 다르다.
→ 즉, 인간이라는 카테고리를 클래스라 보면 각각의 우리는 서로 다른 객체이다.
→ 클래스를 만들 때 모델링을 한다. 모델링은 정의를 내리는 것이다.
→ 속성(attribute)
※ cmd 창에서 attrib 이라고 치면 해당 폴더 내의 파일 속성을 보여준다.
→ 머리색, 피부색은 상태(status)정보, 동작은 행위(behavior) 정보
→ 모든 속성은 상태와 행위(행동)을 가진다. 이를 위해 클래스는 변수 + 함수를 가지며 이를 통해 정보를 가진다.
→ 클래스를 만든다는 것은 분류한다는 것을 뜻한다.
→ 접근 속성
→ 위의 예제에서 struct를 class로 바꾸어 주고
→ 변수는 private: , 함수는 public: 을 붙여주고 들여쓰기를 한뒤 컴파일 하면 에러없이 컴파일 한다.
→ 기본적으로 모든 속성은 private 이다. 이것은 클래스 안에서는 모두 사용할 수 있다.
→ public은 외부에서 접근이 가능하다.
#include<stdlib.h>
class emb // emb 클래스를 부른다.
{
private :
int *item;
int iNum;
public :
bool embInit();
bool embInit(unsigned int uiNum);
void embFree();
};
int main()
{
emb OBJ;
OBJ.embInit(); // 0으로 초기화 하는 함수
OBJ.embInit(300); // 동적할당 및 전체 계수(1200byte)
OBJ.embFree();
return 0;
}
// 스코프 연산자를 붙인다.
bool emb::embInit()
{
item = 0;
iNum = 0;
cout << "embInit" << endl;
return true;
}
bool emb::embInit(unsigned int uiNum)
{
if(0 == uiNum)
{
return false;
}
item = (int *)malloc(sizeof(int)*uiNum);
iNum = uiNum;
cout << "embInit" << endl;
return true;
}
void emb::embFree()
{
free(item);
item = 0;
iNum = 0;
cout << "embFree" << endl;
return ;
}
→ 일반적으로 속성끼리 모아 쓰며, 변수 함수끼리도 각각 모아 쓴다.
→ 클래스 내부에는 변수와 함수 선언만 있는 것이 바람직 하다.
→ 따라서 함수부분을 클래스에서 빼고 함수 선언만 넣어 둘 수 있는데 그냥 컴파일 하면 문제가 발생한다.
→ 왜냐하면 클래스의 밖에 있는 함수의 본체는 클래스 내부에 선언된 함수 원형과는 다르기 때문이다. 따라서 에러가 발생한다.
→ 이를 해결하기 위해서는 함수 본체의 이름 앞에 emb:: 를 붙여줘야 한다.(클래스 이름::)
→ 이렇게 하면 클래스 내부에 선언한 함수 원형이 밖에 있는 함수의 본체를 가리키는 것이 된다. (JAVA는 이 방법이 되지 않으며 클래스 내부에 함수 본체를 넣어야 한다.)
→ 다음으로 위의 예제를 각각 emb.h, emb.cpp, main.cpp 3개의 파일로 분리한다.
#ifndef __EMB_H_ |
#include"emb.h" |
#include<iostream.h> |
→ 위와 같이 파일을 나누어 컴파일 할 수 있다.
→ 보통 클래스는 헤더 파일(emb.h) 함수는 함수끼리 모아서(emb.cpp), 그리고 메인 함수(main.cpp)로 구분한다.
- inline (사용법, 특징)
→ 함수의 이름 앞에 inline을 붙일 수 있다.
→ inline bool emb::embInit() 이라고 사용
→ 이렇게 사용하면 원래는 main에서 호출될때 사용하는 함수의 본체가 main함수 안으로 직접 써지게 된다.
→ 따라서 실행파일의 크기가 커지나 실행속도가 빨라진다.
→ 클래스 안에서는 inline을 사용하지 않는다. 클래스 안에서 함수를 만들면 자동 inline 되기 때문.
→ 따라서 inline 함수는 무조건 헤더 파일에 들어가야 한다.(함수 본체가 밖에 있으면 함수 본체도 헤더에)
→ 메크로 함수보다 유리하다.
- 전역 변수의 사용
→ 만약 함수의 변수가 iNum=0; 이고 전역변수의 이름도 iNum = 100; 이라면 함수내에서 iNum의 값을 읽을 때 0이 읽어진다.
→ 하지만 ::iNum이라고 호출하면 전역변수 100이 읽어진다. 즉, 스코프 연산자를 앞에 붙여서 같은 이름을 가진 전역 변수도 호출이 가능하다.
→ 예제
#include<stdlib.h>
// 객체의 크기는 힙이나 스택에서 얼마만큼의 크기를 가지느냐다
// 따라서 아래 emb 클래스의 크기는 8byte이다.
// 포인터 변수 4byte, int형 4byte
// 함수는 코드영역이므로 호출되었다 사라지므로 용량이 적용 안된다.
class emb // emb 클래스를 부른다.
{
private :
int *item;
int iNum;
public :
bool embInit();
bool embInit(unsigned int uiNum);
void embFree();
};
int iNum = 100;
int main()
{
emb OBJ;
//OBJ.iNum = 100; // 접근속성이 pivate 이므로 에러
OBJ.embInit(); // 0으로 초기화 하는 함수
OBJ.embInit(300); // 동적할당 및 전체 계수(1200byte)
OBJ.embFree();
cout << "main의 iNum = " << iNum << endl;
cout << "class emb의 크기 = " << sizeof(emb) << endl;
return 0;
}
// 스코프 연산자를 붙인다.
bool emb::embInit()
{
item = 0;
iNum = 0;
cout << "embInit" << endl;
cout << "embInit() 내부 변수 iNum = " << iNum << endl;
// 스코프(::)를 변수 이름앞에 붙이면 전역 변수 iNum을 출력한다.
// 만약 class에 iNum이 있더라도 pivate 이므로 스코프로 출력할 수 없다.
cout << "embInit()에서 전역 변수 iNum = " << ::iNum << endl;
return true;
}
bool emb::embInit(unsigned int uiNum)
{
if(0 == uiNum)
{
return false;
}
item = (int *)malloc(sizeof(int)*uiNum);
iNum = uiNum;
cout << "embInit" << endl;
return true;
}
void emb::embFree()
{
free(item);
item = 0;
iNum = 0;
cout << "embFree" << endl;
return ;
}
→ 마지막에 출력한 class emb의 크기를 살펴보면 8byte인 것을 확인할 수 있다.
→ 즉, 포인터 형의 크기 4바이트와 int형 크기 4바이트가 합쳐진 것으로, 함수는 사이즈에 포함되지 않음을 확인할 수 있다.
→ 함수의 인자와 내부변수의 이름이 같을 때 우선순위는 우선 함수 함수 안에 쓰여진 변수(내부 변수)가 높다.
→ 따라서 객체 자체가 자기 자신을 가리키는 포인터(자바는 참조)가 존재한다.
→ this-> or *this. 로 사용할 수 있다.
※ 참조 (자바는 기본적으로 참조형이다.)
→ 자바에서 기본형 int, char, short... 등이며 객체 참조 형은 변수(참조)가 객체를 가리키는 형태
→ 이것은 포인터와 같은 개념이며 자바는 기본형 빼고 전부 포인터 객체 참조와 같은 원리이다.
- 알리아스(메모리를 차지 하지 않는다.)
int main()
{
int A = 100;
int *P = &A;
int &R = A;
printf("A = %X\n", A);
printf("P = %X\n", P);
printf("R = %X\n", R);
printf("A address = %X\n", &A);
printf("P address = %X\n", &P);
printf("R address = %X\n", &R);
R = 99;
printf("A = %X\n", A);
printf("P = %X\n", P);
printf("R = %X\n", R);
return 0;
}
→ 변수 A와 R이 같은 주소와 값을 가지는 것을 확인할 수 있다.
→ 어셈블리 내에서 A와 R을 같이 취급(컴파일 단계)
→ 클래스를 만들 때 참조 변수가 필요하다.
'내장형 하드웨어 > C++' 카테고리의 다른 글
C++ - 객체 소멸 순서, 객체 생성(정적, 동적 할당-malloc, new) (0) | 2011.11.15 |
---|---|
C++ - car class 추가, 다중상속, 유도(derivation), 타입 (0) | 2011.11.14 |
C++ 생성자, 소멸자, 상속 (0) | 2011.11.11 |
C++ (함수 인자의 새로운 용법, 오버로딩 과정 확인) (0) | 2011.11.09 |
객체지향, C++ 첫시간(cout, endl, 오버로딩) (0) | 2011.11.08 |