C++ 클래스
C++ 클래스와 관련한 내용을 작성한다.
의미 정의
클래스 = 멤버변수 + 멤버함수
클래스(Class) = 속성(Attribute) + 방법, 기능(Method)
- 데이터 추상화(Data Abstraction) : 사물을 기능적, 데이터적 측면으로 재정의 하는 것
- 클래스화 : 추상화된 데이터를 통해 사용자 정의 자료형을 정의하는 것(클래스화)
객체(Object): 클래스를 이용하여 정의된 자료형의 변수
-
인스턴스화(instantiation, 객체화) : 클래스를 기반으로 객체를 생성하는 것
클래스 멤버 접근
내부, 외부 접근
. 내부 접근 : 같은 클래스 내에 존재하는 멤버에 의한 접근
. 외부 접근 : 내부 접근 외의 모든 접근
Public, Protected, Private
. Public : 클래스 외부 접근 허용
. Private : 클래스 내부 접근만 허용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using std::cout;
using std::endl;
const int OPEN=1;
const int CLOSE=2;
class Door{
private:
int state;
public:
void Open(){
state = OPEN; // 내부 접근
}
void Close(){
state = CLOSE; // 내부 접근
}
void ShowState(){
cout << "현재 문 상태 :";
cout << ((state==OPEN)? "OPEN" : "CLOSE") << endl;
}
};
int main(){
Door d;
// d.state = OPEN; // 외부 접근(private라 접근 불가) - 오류 발생
d.Open(); // 외부 접근
d.ShowState(); // 외부 접근
return 0;
}
멤버 함수 외부 접근 및 인라인(inline)
. 외부 접근은 ::
연산자를 활용함
인라인
. 함수 선언, 혹은 정의 앞에 inline
키워드를 함수의 앞단에 추가
. 컴파일된 함수 코드가 프로그램의 코드 안에 직접 삽입됨
: 성능의 향상이 가능함
: 메모리 사용 측면에서 인라인 함수가 일반 함수보다 불리
: 자료형에 독립적이지 못함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
using std::cout;
using std::endl;
const int OPEN=1;
const int CLOSE=2;
// ****** Door 클래스 `선언` ****** //
class Door{
private:
int state;
public:
void Open();
void Close();
void ShowState();
};
// ****** Door 클래스 `선언` ****** //
// ****** Door 클래스 멤버 함수 `정의` ****** //
void Door::Open(){
state = OPEN; // 내부 접근
}
void Door::Close(){
state = CLOSE; // 내부 접근
}
void Door::ShowState(){
cout << "현재 문 상태 :";
cout << ((state==OPEN)? "OPEN" : "CLOSE") << endl;
}
// 함수를 인라인 하기 위해서는, inline 키워드를 붙여주면 됨
// ex) void Door::Open() -> inline void Door::Open()
// ****** Door 클래스 멤버 함수 `정의` ****** //
int main(){
Door d;
d.Open();
d.ShowState();
return 0;
}
정보은닉(Information Hiding)
일반적으로, 객체의 외부에서 객체 내에 존재하는 멤버 변수에 직접 접근을 허용하지 않도록 함
. 클래스 선언 시, public, private 등의 선언을 하지 않고, 멤버 변수를 정의하면 private
가 기본으로 설정됨
. 아래 예제와 같이, 멤버 변수의 접근을 멤버 함수로 제한하고, 함수 안에서 조건을 따지는 형태로 구성하는 것으로 보임
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
class Point
{
// public, private를 지정하지 않으면 private가 기본으로 설정됨
int x; // x 좌표 범위 0-100
int y; // y 좌표 범위 0-100
public:
int GetX(){return x;}
int GetY(){return y;}
void SetX(int _x); // == void SetX(int);
void SetY(int _y); // == void SetY(int);
};
void Point::SetX(int _x){
if(_x < 0 || _x > 100) // 경계 검사
{
cout << "잘못된 X 좌표 입력" << endl;
return;
}
x = _x; // 정보 은닉 형태, 클래스 멤버 변수에 대한 내부 접근만 허용
}
void Point::SetY(int _y){
if(_y < 0 || _y > 100) // 경계 검사
{
cout << "잘못된 Y 좌표 입력" << endl;
return;
}
y = _y; // 정보 은닉 형태, 클래스 멤버 변수에 대한 내부 접근만 허용
}
int main(){
int x, y;
cout <<"좌표값 입력: ";
cin >> x >> y;
Point p;
p.SetX(x);
p.SetY(y);
cout << "입력 값 문제 없음" << endl;
return 0;
}
캡슐화(Encapsulation)
관련 있는 데이터와 함수를 하나의 단위로 묶음
생성자(Constructor) 및 소멸자(Destructor)
객체 생성의 2 단계 : 메모리 할당 → 생성자 호출
생성자는 객체 생성 시 원하는 값으로 초기화 하기 위해 사용됨
생성자 내에서 메모리를 동적 할당하는 경우, 이를 해제하기 위해서 반드시 소멸자를 정의해야 함
특징
- 함수
- 클래스의 이름과 같은 이름
- 리턴 타입 선언 없음
- 리턴 값 없음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <string.h> // strcpy 사용하기 위한 헤더파일
using std::cout;
using std::endl;
const int SIZE=20;
class Person{
char name[SIZE];
char phone[SIZE];
int age;
public:
Person(char * _name, char* _phone, int _age);
void ShowData();
};
Person::Person(char * _name, char* _phone, int _age)
{
strcpy(name, _name);
strcpy(phone, _phone);
age = _age;
}
void Person::ShowData()
{
cout << " name : " << name << endl;
cout << " phone : " << phone << endl;
cout << " age : " << age << endl;
}
int main()
{
Person p("ABC", "010-123-4567", 30); // 객체 생성 동시에, 초기화 가능
// Person p = ("ABC", "010-123-4567", 30); 의 형태로도 생성 가능
p.ShowData();
return 0;
}
Default 생성자
-
생성자를 정의 하지 않으면 기본(Default) 생성자가 자동 삽입됨
-
생성자도 함수이므로 오버로딩 가능
-
생성자도 함수이므로 기본(Default) 매개 변수의 설정이 가능
. 기본적으로 Default 매개 변수를 지정한 생성자 구성을 활용하도록 함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
using std::cout;
using std::endl;
class Point
{
int x, y;
public:
Point();
Point(int _x, int _y);
void ShowData();
};
Point::Point(){ // 생성자도 함수 오버로딩 가능
x=y=0;
}
Point::Point(int _x=0, int _y=0) // 생성자에 Default 매개 변수 지정
{
x = _x; y = _y;
}
void Point::ShowData()
{
cout << x << " " << y << endl;
}
int main()
{
Point p1(10, 20);
p1.ShowData();
Point p2; // Default 생성자를 호출할 경우, Default 매개 변수가 지정되어 있어
// 오류가 발생하지 않음
// Default 매개 변수가 지정이 안되어 있을 경우, Default 생성자를 찾을 수 없어 오류 발생
p2.ShowData();
}
멤버 이니셜라이져(Member initialization list, 멤버 초기화 리스트)
. 생성자의 역할인 멤버 초기화를 수행함
. ` : ` 연산자를 활용하여 구현함
. 객체 생성 이전에 초기화가 이루어진다
사용해야할 상황
- default 생성자 없을 때
- 클래스가 레퍼런스를 멤버로 가질 때
- 상수 멤버를 초기화 할 때
- 생성자 파라미터의 이름이 데이터 멤버와 같을 때
- 부모 클래스의 멤버변수 초기화(상속)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
......
class Point
{
private:
int x;
int y;
public:
// 일반적 초기화(생성자)
/*
point(int _x, int _y){
x = _x;
y = _y;
}
*/
// 멤버 이니셜라이져
point(int _x, int _y): x(_x), y(_y){}
};
.......
Default 소멸자
객체 소멸의 2 단계 : 소멸자 호출 → 메모리 해제
객체를 소멸시키기 전, 할당된 메모리를 해제하는 등의 일련의 과정을 수행하기 위함
-
함수
-
~
연산자가 클래스 이름 앞에 붙은 형태로 선언 -
리턴타입 선언 없음
-
리턴 값 없음
-
매개 변수 없음
-
오버로딩 불가
-
Default 매개 변수 선언 불가
-
소멸자를 정의 하지 않으면 기본(Default) 소멸자가 자동 삽입됨
일반적으로, 생성자 내에서 동적 할당을 수행하는 경우, 이를 해제하기 위해 반드시 소멸자를 정의해야 함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <string.h>
using std::cout;
using std::endl;
class Person{
char * name;
char * phone;
int age;
public:
Person(char * _name, char* _phone, int _age);
~Person(); // 소멸자
void ShowData();
};
Person::Person(char * _name, char* _phone, int _age)
{
// new 연산자 이용한 메모리 동적할당
name = new char[strlen(_name) + 1];
strcpy(name, _name);
// new 연산자 이용한 메모리 동적할당
phone = new char[strlen(_phone) + 1];
strcpy(phone, _phone);
age = _age;
}
Person::~Person()
{
// delete 연산자 이용한 메모리 할당 해제
delete [] name;
delete [] phone;
}
void Person::ShowData()
{
cout << " name : " << name << endl;
cout << " phone : " << phone << endl;
cout << " age : " << age << endl;
}
int main()
{
Person p("ABC", "010-123-4567", 30);
p.ShowData();
return 0;
}
. 위 예제의 경우, 메모리 동적할당 형상
클래스 배열
객체 배열
객체 배열이 생성되기 위해서는 void 생성자의 호출이 요구됨
객체 포인터 배열1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
using std::cout;
using std::endl;
class Point{
int x;
int y;
public:
Point(){
cout << "Default Constructor!" << endl;
x=y=0;
}
Point(int _x, int _y){
x= _x;
y= _y;
}
int GetX(){ return x; }
int GetY(){ return y; }
void SetX(int _x) { x = _x; }
void SetY(int _y) { x = _y; }
};
int main(){
Point * arr[5]; // 포인터 배열(배열 요소로 포인터를 가짐)을 선언
for(int i=0; i<5; i++)
{
arr[i] = new Point( i*2, i*3); // 메모리 동적 할당 후, 객체 포인터 할당
// i*2, i*3 을 인자(정수형)로 받을 수 있는 Point 클래스 생성자 호출하면서, Point 객체 생성
}
for(int j=0; j<5; j++)
{
cout << " x: " << arr[j]->GetX() << " ";
cout << " y: " << arr[j]->GetY() << endl;
}
// 할당된 메모리 해제
for(int k=0; k<5; k++)
{
delete arr[k];
}
return 0;
}
this 포인터
. 자기 참조 포인터
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
using std::cout;
using std::endl;
class Data{
int a;
int b;
public:
Data(int a, int b){ // 클래스 멤버 변수와 Data()함수의 매개변수의 이름이 같음
// a = a; // 이 경우, 클래스 멤버 변수의 값을 변경할 수 없음
this->a = a;
// b = b;
this->b = b;
}
void ShowData(){
cout << a << " " << b << endl;
}
};
int main(void)
{
Data d(10, 20);
d.ShowData();
return 0;
}
friend 선언
. private로 선언된 멤버변수에 대해 외부 접근을 허용하는데 사용됨
. friend 선언은 단방향성 특성을 지님
전역 함수에 대한 friend 선언
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using std::cout;
using std::endl;
class counter{
int val;
public:
counter(){
val = 0;
}
void Print() const{
cout << val << endl;
}
friend void SetX(counter& c, int val);
};
void SetX(counter& c, int val)
{
c.val = val;
}
int main()
{
counter cnt;
cnt.Print();
SetX(cnt, 2000);
cnt.Print();
return 0;
}
클래스에 대한 friend 선언
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
using std::cout;
using std::endl;
class First{
private:
int data;
friend class Second;
public:
First(){
data = 0;
}
void Print(){
cout << data << endl;
}
};
class Second{
public:
void SetData(First& first, int val){
first.data = val; // class First의 private 영역 접근
}
};
int main()
{
First first;
Second second;
first.Print();
second.SetData(first, 200);
first.Print();
return 0;
}
-
포인터 배열은 배열 요소로 포인터를 가짐, ‘배열 포인터’ 는 주로 2,3차원 배열에서 배열을 가리키기 위한 포인터를 지칭 ↩