▣ 객체지향 프로그래밍?
● 객체(object)란?
▷ 소프트웨어 시스템 안의 어떠한 대상을 표현한 것으로 다음의 역할을 수행한다.
- 정해진 처리를 수행한다. --> 행위, 메소드, 멤버함수
- 처리 과정에 따라 내부 상태가 변화할 수 있다. --> 속성, 데이터 멤버
- 다른 객체와 상호작용 할 수 있다. --> 메시지 전달(멤버함수 호출)
▷ 아래의 포스트에서 정의했던 원 객체를 예로 들면 다음과 같이 역할에 따라 분류할 수 있다.
2020/08/26 - [Programming/C++] - [초급] C++ 언어 기초 (3) - 구조체와 클래스
- 행위: init(), area(), display(), chkOverlap()
- 속성: center, radius
● 클래스(class)란?
▷ 객체의 설계도로 객체가 포함할 속성에 대한 명세와 메소드의 정의를 포함한다.
▷ 위의 이전 포스트에서 정의했던 CircleClass를 예로 들 수 있다.
● 캡슐화(encapsulation)란?
▷ 객체 내부의 상세한 구현 부분과 외부 사용자의 관점을 분리하는 행위를 말한다.
▷ 내부 속성 등 구현에 대한 부분은 공개하지 않고 객체 외부에서는 공개된 인터페이스를 통해 객체를 사용할 수 있다.
▷ 캡슐화의 예시
우리는 TV의 동작 원리를 모르지만 TV를 켜고 채널을 돌리는 등의 조작 단추를 통한 기능을 이용할 수 있다.
▷ 캡슐화의 장점
- 소프트웨어의 유지보수가 용이하다.
--> 프로그램의 다른 부분에 영향을 미치지 않고 객체 내부를 수정할 수 있다. - 재사용이 용이하다.
--> 잘 설계된 캡슐화 객체는 다른 응용에서도 재사용 가능하여 소프트웨어 개발 비용을 줄일 수 있다.
▣ 클래스 선언과 객체 정의
● 클래스 선언문의 형식
class ClassName {
가시성_지시어_1:
데이터 멤버 또는 멤버함수 리스트;
가시성_지시어_2:
데이터 멤버 또는 멤버함수 리스트;
......
};
● 가시성 지시어
▷ 클래스의 멤버가 공개되는 범위를 나타낸다.
▷ 종류: private, public, protected
가시성 지시어 | 공개 범위 | 용도 |
private (디폴트) |
- 소속 클래스의 멤버함수 - 친구 클래스의 멤버함수 및 친구함수 - 그 외의 범위에는 비공개! |
- 정보 은닉을 위해 사용된다. - 클래스의 구현을 위한 내부 상태(데이터 멤버)는 일반적으로 private으로 지정한다. |
public | - 전 범위 | - 주로 외부에 제공할 인터페이스를 공개하기 위해 사용된다. |
▷ 예시
class CircleClass {
double center;
double radius;
public:
void init(double cx, double cy, double r) { ... }
double area() const { ... }
...
};
● 객체 정의와 사용
▷ 객체 정의 형식
ClassName objName;
ClassName objName1, objName2, ... ;
/*Example*/
int main()
{
CircleClass c1, c2;
...
}
▷ 객체 사용 형식
객체 이름에 멤버선택 연산자(.)를 사용하여 객체의 멤버를 액세스한다.
cin >> objName.dataMember;
objName.memFunc( ... );
객체의 멤버함수 안에서 그 객체에 속한 멤버를 사용할 때는 멤버 이름만으로 액세스한다.
▷ 객체 사용 예시
class CircleClass {
....
public:
...
bool chkOverlap(const CircleClass& c) const { ... }
};
int main() {
CircleClass c1, c2;
if (c1.chkOverlap(c2))...
}
▣ 생성자
● 생성자(constructor)란?
▷ 객체가 생성될 때 수행할 작업을 정의하는 특수한 멤버함수
▷ 매개변수를 통해 인수를 전달할 수 있고, 다중정의 할 수 있다.
● 생성자의 특성
▷ 클래스의 이름을 사용해서 선언한다.
▷ 생성자 머리에 반환 자료형을 표시하지 않고, return 명령으로 값 반환할 수 없다!
▷ 생성자를 public으로 선언해야 클래스 외부에서 객체를 생성할 수 있다.
● 생성자 선언 형식
class ClassName {
...
public:
/*생성자 시작*/
Classname(fParameterList){
.... //객체 생성을 위한 준비 작업
}
/*생성자 끝*/
...
};
● 생성자의 예
/*Counter.h*/
class Counter {
int value;
public:
//생성자
Counter()
{ value = 0;}
void reset()
{ value = 0;}
void count()
{ ++value;}
int getValue() const
{return value;}
};
/*CntMain.cpp*/
#include <iostream>
#include "Counter.h"
using namespace std;
int main()
{
Counter cnt;
cout << "계수기의 현재 값 : "
<< cnt.getValue() << endl;
cnt.count();
cnt.count();
cout << "계수기의 현재 값 : "
<< cnt.getValue() << endl;
return 0;
}
● 초기화 리스트
▷ 초기화 리스트는 생성자의 머리에 데이터 멤버를 초기화하는 값들을 나열한 리스트를 말한다.
▷ '데이터멤버이름{초깃값}' 형태로 초깃값을 지정한다.
/*Counter.h*/
class Counter {
int value;
public:
Counter() : value{0} { } //생성자
...
};
▣ 소멸자
● 소멸자(destructor)란?
▷ 객체가 소멸될 때 수행할 작업을 정의하는 특수한 멤버함수이다.
● 소멸자의 특성
▷ 클래스의 이름에 '~'를 붙여 선언한다.
▷ 소멸자 머리에 반환 자료형을 표시하지 않고 return 명령으로 값을 반환할 수 없다.
▷ 매개변수를 포함할 수 없다.
▷ 다중정의할 수 없고, 클래스에 하나만 정의한다.
▷ public으로 선언하는 것이 일반적이다.
▷ 상속을 통해 파생 클래스를 정의하는 경우 virtual을 지정해서 가상함수가 되도록 하는 게 좋다.
● 소멸자 선언 형식
class ClassName {
....
public:
/*생성자*/
ClassName(fParameterList){
... //객체 생성을 위한 준비 작업
}
/*소멸자*/
ClassName() {
... //객체 제거를 위한 정리 작업
}
...
};
▣ 직접 실습해보기!
● Person 클래스를 만들어보자
▷ Person 클래스는 사람을 나타내는 클래스로, "어디에 사는 누구입니다."라고 자신을 알릴 수 있어야 하고, 주소를 변경할 수 있어야 한다.
● Person 클래스 명세
▷ 행위
메소드 | 비고 |
Person(char *name, char *addr) | 생성자 |
~Person() | 소멸자 |
void print() | '...에 사는 ...입니다'라고 출력한다 |
void chAddr(char *newAddr) | 주소를 변경한다 |
▷ 속성
속성 | 비고 |
char *name | 이름을 저장한다 |
char *addr | 주소를 저장한다 |
● Person 클래스 선언 - Person.h
▷Person.h의 클래스 선언문에서는 멤버함수들의 원형만 선언하고 있다.
#ifndef PERSON_H_INCLUDED
#define PERSON_H_INCLUDED
class Person{ //클래스 Person의 선언 시작
char *name; //이름을 저장하는 데이터멤버
char *addr; //주소를 저장하는 데이터멤버
public: //public 멤버함수
Person(const char *name, const char *addr); //생성자
~Person(); //소멸자
void print() const; //이름과 주소 출력
void chAddr(const char *newAddr); //주소 변경
};
#endif //PERSON_H_INCLUDED
● Person 클래스 정의 - Person.cpp
▷Person.cpp에서는 클래스의 멤버함수를 정의한다. 클래스 선언문은 Person.h에서 선언했기 때문에 #include로 클래스 선언문을 삽입해주고, 각각의 멤버함수들이 Person 클래스 소속임을 명시하기 위해 'Person::'을 기입한다.
▷'this->'로 객체 자기 자신을 가리키는 포인터를 사용해서 함수 내부에서 형식 매개변수를 가리키는 것이 아닌 데이터 멤버를 지칭하게끔 해준다.
#include <iostream>
#include <cstring>
#include "Person.h"
using namespace std;
Person::Person(const char *name, const char *addr)
{
//이름을 저장할 공간 할당
this->name = new char[strlen(name)+1];
//데이터 멤버 name에 이름을 복사
strcpy(this->name, name);
//주소를 저장할 공간 할당
this->addr = new char[strlen(addr)+1];
//데이터 멤버 addr에 주소를 복사
strcpy(this->addr, addr);
cout << "Person 객체 생성함( " << name << ")" << endl;
}
Person::~Person() //소멸자
{
cout << "Person 객체 제거함(" << name << ")" << endl;
delete [] name; //이름 저장공간 반납
delete [] addr; //주소 저장공간 반납
}
void Person::print() const
{
cout << addr << "에 사는 " << name << "입니다." << endl;
}
void Person::chAddr(const char *newAddr)
{
delete [] addr; //기존 공간 반납
//새로운 주소에 맞는 공간 할당
addr = new char[strlen(newAddr)+1];
strcpy(addr, newAddr); //데이터멤버 addr에 새로운 주소를 복사
}
● 실행 - Main 함수
▷Person 객체를 new 연산자를 통해 동적 할당한다.
▷생성자가 동작하며 화면에 출력되는 메시지를 통해 Person 객체가 생성되는 걸 알 수 있다. 마찬가지로 소멸자도 동작하는 걸 볼 수 있다.
▷구조체에서와 마찬가지로 '->' 포인터를 사용해서 객체의 멤버에 액세스한다.
int main()
{
Person *p1 = new Person("박지민", "서울시 강남구");
Person *p2 = new Person("김태형", "서울시 서초구");
Person *p3 = new Person("민윤기", "서울시 종로구");
cout << endl;
p1->print();
p2->print();
p3->print();
cout << endl << "주소 변경:";
p2->chAddr("서울시 용산구");
p2->print();
cout << endl;
delete p1;
delete p2;
delete p3;
return 0;
}
'Programming > C++' 카테고리의 다른 글
[C++] const 사용 시 주의점 (0) | 2020.08.27 |
---|---|
[C++] 헤더파일(.h) 내용 중복 include 방지 방법 (0) | 2020.08.27 |
C++ 언어 기초 (5) - 함수 (0) | 2020.08.26 |
C++ 언어 기초 (4) - 포인터, 동적 메모리 할당, 참조 (0) | 2020.08.26 |
C++ 언어 기초 (3) - 구조체와 클래스 (0) | 2020.08.26 |