본문 바로가기
Programming/C++

C++ 언어 기초 (6) - 클래스와 객체

by 롱일스 2020. 8. 27.
반응형

▣ 객체지향 프로그래밍?

 ●  객체(object)란?

  ▷ 소프트웨어 시스템 안의 어떠한 대상을 표현한 것으로 다음의 역할을 수행한다.

  •    정해진 처리를 수행한다. --> 행위, 메소드, 멤버함수
  •    처리 과정에 따라 내부 상태가 변화할 수 있다. --> 속성, 데이터 멤버
  •    다른 객체와 상호작용 할 수 있다. --> 메시지 전달(멤버함수 호출)

  ▷ 아래의 포스트에서 정의했던 원 객체를 예로 들면 다음과 같이 역할에 따라 분류할 수 있다.

2020/08/26 - [Programming/C++] - [초급] C++ 언어 기초 (3) - 구조체와 클래스

  •    행위: init(), area(), display(), chkOverlap()
  •    속성: center, radius

 

 ●  클래스(class)란?

  ▷ 객체의 설계도로 객체가 포함할 속성에 대한 명세와 메소드의 정의를 포함한다.

  ▷ 위의 이전 포스트에서 정의했던 CircleClass를 예로 들 수 있다.

 

 ●  캡슐화(encapsulation)란?

  ▷ 객체 내부의 상세한 구현 부분과 외부 사용자의 관점을 분리하는 행위를 말한다.

  ▷ 내부 속성 등 구현에 대한 부분은 공개하지 않고 객체 외부에서는 공개된 인터페이스를 통해 객체를 사용할 수 있다.

  ▷ 캡슐화의 예시 

   우리는 TV의 동작 원리를 모르지만 TV를 켜고 채널을 돌리는 등의 조작 단추를 통한 기능을 이용할 수 있다. 

  ▷ 캡슐화의 장점

  1.  소프트웨어의 유지보수가 용이하다.
        --> 프로그램의 다른 부분에 영향을 미치지 않고 객체 내부를 수정할 수 있다.
  2.  재사용이 용이하다.
        --> 잘 설계된 캡슐화 객체는 다른 응용에서도 재사용 가능하여 소프트웨어 개발 비용을 줄일 수 있다.

 

▣ 클래스 선언과 객체 정의

 ●  클래스 선언문의 형식

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;
}

실행 결과 화면

 

728x90
반응형