객체의 뜻과 클래스 만들기
객체란 "하나의 대상"이라고 생각합니다. 그 하나의 대상이 가지는 내부 구조는 외부로부터 보호받고, 다른 대상과 상호작용을 하지요. 이러한 객체만의 특징을 "캡슐화"라고도 합니다.
C++ 객체는 자신의 상태(state)를 보여주는 멤버 변수와 행동(behavior)을 구현하는 코드인 멤버 함수로 구성하고, 이런 객체를 정의하는 틀을 "클래스"라고 합니다.
즉, 클래스의 역할은 멤버 변수 메모리와 멤버 함수 코드를 가진 객체를 만들어 객체가 역할을 수행하게끔 하는 것이고, 컴파일이 끝나면 사라집니다. 클래스와 객체는 비슷해 보이지만, 의미와 역할은 다르기에 구분해야 합니다.
class pencilcase{
int pencil_1;
int pencil_2;
int eraser;
pubilc:
void write();
void erase();
};
// "pencilcase"이라는 객체를 만드는 pencilcase 클래스
다음 pencilcase 클래스는 여러 개의 pencilcase 객체를 생성할 수 있으며 이러한 객체들의 멤버 변수와 맴버 함수 코드는 같지만 역할은 다 다릅니다.
클래스의 구성과 만들기
클래스는 C언어의 구조체와 비슷하게 개발자가 정의하는 새로운 데이터타입이며 class키워드를 이용해 선언합니다. 일반적으로 클래스 구현부와 클래스 선언부로 구성되어 있습니다.
- 클래스 선언부
class pencilcase{ // pencilcase라는 이름의 클래스 선언
... // 맴버 변수와 맴버 함수의 '원형' 선언
};
클래스 선언부 내부에는 int pencil_name = 9;처럼 초기화할 수 있고, 멤버함수는 int write();처럼 원형으로 선언됩니다.
public: 같은 접근 지정자가 선언되는데 이는 클래스의 멤버들이 다른 클래스와의 통신을 위해 공개를 어느 정도 할지를 정해줍니다. public : 클래스 외부로부터 접근 허용, private: 클래스 외부로부터 접근 불가능, protected: 접근 지정 없음
자세한 부분은 나중에 다룹니다.
- 클래스 구현부
int pencilcase :: write(){
return 10*pencil_name;
}
클래스 구현부는 클래스 선언부에 선언한 멤버 함수의 코드를 구현하는 부분입니다. 다음은 write( ) 함수의 구현이며, 반환 타입과 소속된 '::'연산자를 이용해 나타냅니다.
뜻은 "pencilcase 클래스의 멤버 함수 write( )를 실행하면 pencil_name에 저장된 값에 10을 곱한 값을 반환한다."입니다.
※ '::'는 범위 지정자로 "클래스 소속하"라는 의미를 가집니다.
다음은 클래스를 이용한 사각형 면적을 구하는 프로그램 코드입니다.
#include <iostream>
using namespace std;
class Rectangle {
public:
int width;
int height;
int print_area();
};
int Rectangle::print_area() {
return width * height;
}
int main() {
Rectangle A;
A.width = 3;
A.height = 6;
cout << "사각형 A의 면적 : " << A.print_area() << endl;
Rectangle B;
B.width = 5;
B.height = 10;
cout << "사각형 B의 면적 : " << B.print_area() << endl;
return 0;
}
다음 코드를 보면 알 수 있듯 객체를 선언한 후 해당 개체의 멤버를 사용하려면 "객체이름. 멤버"로 코드를 작성해야 함을 알 수 있습니다.
생성자와 소멸자
생성자는 객체 생성 시 객체를 초기화하는 방식을 의미합니다. 클래스로부터 만들어지는 각 객체에 따라 초기화되는 값이 다를 수 있기 때문에 생성자를 사용합니다.
예시) a를 받는 객체는 0으로 초기화하는 객체 / 아무런 변수를 안 받으면 1로 초기화하는 객체
소멸자는 객체가 역할을 다해 사라질 때 필요한 작업을 마무리하기 위해 사용합니다.
예시) 메모리 반환, 열어놓은 파일 닫기, 연결된 네트워크 해제 등
- 생성자
class rectangle {
rectangle(); // 매개변수가 없는 생성자
rectangle(int width,int height); // 매개변수를 받는 생성자
};
rectangle::rectangle(){
...
}
// 생성자 함수 구현_1
rectangle::rectangle(int width,int height){
...
return;
}
// 생성자 함수 구현_2
다음과 같이 선언하고, 주의할 점은 클래스 내에 생성자를 여러 개 둘 수 있으나, 생성되는 객체의 특성에 따라 하나만 실행되며, 생성자의 이름은 클래스 이름과 동일하게 작성되어야 합니다.
생성자 함수에서는 클래스를 만드는 특성에 관련되기에 리턴을 받지 않습니다. 때문에 리턴 타입은 적지 않으며 함수의 종료를 알려주는 return;은 써도 되지만 return 0;과 같이 리턴 값을 쓰면 안 됩니다.
위의 코드를 보면 같은 클래스에서 나온 생성자이기에 생성자 함수 내에 비슷한 코드가 중복되는 경우가 있는데, 이를 간소화시키기 위한 "타깃 생성자"와 "위임 생성자"를 사용합니다.
타겟 생성자는 객체 초기화를 전담하는 생성자이고, 위임 생성자는 타깃생성자를 호출하여 객체를 초기화하는 생성자입니다.
rectangle::rectangle(){
width = 1; height=1;
cout << "넓이 : " << width*height << endl;
}
rectangle::rectangle(int a,int b){
width = a; height=b;
cout << "넓이 : " << width*height << endl;
}
// 위의 두 코드를 아래와 같이 간략하게 작성 할수 있습니다.
retangle::rectangle() : rectangle(2,3){} // 위임 생성자 : rectangle(int a,int b) 호출
rectangle::rectangle(int a,int b){
width = a; height=b;
cout << "넓이 : " << width*height << endl;
} // 타겟 생성자
생성자의 역할은 객체의 초기화이기 때문에 멤버 변수도 초기화가 가능합니다. 다음은 멤버 변수를 초기화하는 스타일을 몇 개를 소개합니다.
class pencilcase{
int x,y;
public:
pencilcase();
pencilcase(int a, int b);
};
// 1. 일반적인 생성자 함수 구현
pencilcase::pencilcase() {x=1;, y=1;}
pencilcase::pencilcase(int a, int b) {x=a; y=b;}
// 2. 생성자 서두에 초기값 대입으로 초기화
pencilcase::pencilcase(): x(0), y(0) {
}
pencilcase::pencilcase(int a, int b) : x(a), y(b) {
}
// 혹은 아래와 같이 표현할 수 있습니다.
pencilcase::pencilcase(int a) : x(a), y(b) {
}
// 3. 클래스 선언부에서 직접 초기화
class pencilcase{
int x=1, y=1;
...
};
앞에서 생성자를 사용하지 않고도 컴파일이 되는 코드를 작성했었는데, 이경우 컴파일러가 자동으로 클래서 내에 기본 생성자 "rectangle();"를 삽입해 주기 때문에 문제가 발생하지 않습니다.
하지만 생성자를 하나라도 클래스 내에 선언했다면, 자동으로 생성자를 삽입해주지 않기 때문에 생성자를 하나 적으면 나머지의 경우에도 생성자를 선언해 주셔야 합니다.
위의 코드로 예시를 들면 클래스 내에 pencilcase()만 선언하고, 메인함수에 pencilcase(3,4)로 입력받는 다면 오류가 생길 수 있다는 내용입니다.
- 소멸자
소멸자는 객체가 소멸되는 시점에서 자동으로 호출되는 클래스의 멤버 함수입니다.
class rectangle{
rectangle();
rectangle(int a,int b);
~rectangle(); // 소멸자 함수 선언
};
rectangele::~rectangle(){
...
} //소멸자 함수 구현
소멸자는 다음과 같이 클래스 내에서는 " ~클래스이름( ) "형태이고, 객체의 마무리 작업을 위해 존재하기에 리턴을 받지 않아 리턴 타입을 적으면 안 됩니다.
소멸자 함수 내에서는 소멸자가 실행했을 때 동작의 코드를 작성할 수 있습니다.
또한 생성자와는 달리 한 클래스당 하나씩 존재하며, 클래스에 소멸자를 따로 선언하지 않는 다면 자동으로 컴파일러가 생성합니다.
int main() {
rectangle a;
rectangle b(2,3);
return 0;
}
다음 코드가 실행된다면 C++은 순서대로 읽으면 됩니다. main()에서 a, b순서로 객체가 생성되고, return 0;문이 실행되면 생성된 반대 순으로 b, a객체의 ~rectangle() 소멸자가 실행되어 소멸됩니다.
- 생성자와 소멸자의 실행 순서
변수와 비슷하게 함수 내에 선언된 객체를 지역 객체, 함수 밖에 선언된 객체를 전역 객체라고 합니다. 지역 변수, 전역 변수처럼 함수가 실행되면 지역 객체가 만들어지고 함수가 종료되면 지역객체의 소멸자가 실행됩니다. 전역객체는 프로그램이 실행되면 생성되고 main함수가 종료되면 소멸자가 실행됩니다.
지역 객체나 전역 객체는 생성된 반대순으로 소멸합니다.
아래는 앞에서 배운 생성자와 소멸자를 이용해 각 사각형의 객체의 생성과 소멸을 나타낸 코드입니다.
#include <iostream>
using namespace std;
class Rectangle {
public:
Rectangle();
Rectangle(int a, int b);
int width;
int height;
~Rectangle();
int print_area();
};
int Rectangle::print_area() {
return width * height;
}
Rectangle::Rectangle(): Rectangle(1,1){ }
Rectangle::Rectangle(int a, int b) {
width = a, height = b;
cout << "너비 : " << width << " 높이 : " << height << "인 사각형 생성" << endl;
}
Rectangle::~Rectangle() {
cout << "너비 : " << width << " 높이 : " << height << "인 사각형 소멸" << endl;
}
Rectangle a_1(1, 2);
Rectangle a_2(3, 4);
void rectangle_make(){
Rectangle b_1(4,5);
Rectangle b_2(1, 1);
}
int main() {
Rectangle c_1(5,6);
Rectangle c_2;
rectangle_make();
return 0;
}
'프로그래밍 > C++' 카테고리의 다른 글
[C++] 4장. #2 this 포인터와 string 클래스 (0) | 2023.12.08 |
---|---|
[C++] 4장. #1 객체 포인터와 객체 배열 (0) | 2023.12.04 |
[C++] 3장. #2 접근 지정자와 인라인 함수 (1) | 2023.12.03 |
[C++] 2장. #2 입력 받기 (0) | 2023.11.30 |
[C++] 2장. #1 기본 요소 (0) | 2023.11.28 |