07.OOP
절차적 프로그래밍 (Procedural Programing)
프로그램이 수행하는 일련의 작업을 기준으로하는 프로그래밍 패러다임
작업의 구현 = 함수 / 함수의 집합 = 프로그램
데이터와 작업이 분리되어 있는 개념
데이터는 작업의 실행을 위해 매개변수로 전달될 뿐
이해가 쉬운 방식
절차적 프로그래밍의 단점
함수 데이터의 구조를 정확히 알아야 함
→ 데이터가 변하면, 함수의 수정이 필요
→ Tightly coupled
프로그램의 규모가 커지면
→ 이해하기 어렵고
→ 유지 /보수하기 어렵고
→ 확장하기 어렵고
→ 디버깅하기 어렵고
→ 코드를 재사용하기 어렵고
→ 오동작할 확률이 커진다.
객체지향 프로그래밍(Object Oriented Programming)
절차적 프로그래밍의 단점을 극복하기 위해 제안된 프로그래밍 패러다임 중의 하나
C++, C#, Java등에서는 이러한 방식을 손쉽게 구현할 수 있는 언어의 문법을 제공
→ 함수형 프로그래밍 등 새로운 패러다임을 적용할 수 있도록 언어는 계속 확장 될 수 있다.
클래스와 객체를 기반으로 함
데이터와 작업을 하나로 묶어서 표현
객체지향 프로그래밍의 특징
1 캡슐화
→ 객체는 (데이터) + (데이터를 기반으로 한 동작인 함수)을 구현
2 정보 은닉
→ 사용자는 내부 구현에 대해 알 필요도 없고, 알아서도 안됨
→ 잘못된 사용 및 수정을 방지
→ 사용자는 외부로 노출된 인터페이스만 활용 가능
→ 테스트, 디버깅, 유지보수, 확장이 용이해짐
3 상속
4 다형성
절차적 프로그래밍의 상위 호환이 아니다
잘 설계된 절차적 프로그램 > 잘못 설계된 객체지향 프로그램
모든 문제에 어울리는 설계 방안이 아님
모든 대상이 클래스로 치환되는 것은 아님
객체지향 프로그램은 어렵다, 특히 C++
문제를 분석하여 좋은 설계 사양서를 만들어야한다.(어렵다)
성능에서 손해를 보거나, 복잡한 코드가 작성되기도 함
Classes and Objects
클래스
객체(object)가 생성되기 위한 틀
→ 객체가 가져야 할 데이터와 기능을 정의
사용자 정의 “자료형” (user-defined data-type)
멤버 변수를 가짐(데이터)
→ 속성(property, attribute), 필드(field), 클래스 변수(class variable) → 멤버 변수의 또다른 용어
멤버 함수를 가짐(함수, 동작)
→ Method → 멤버 함수의 또다른 용어
데이터 함수를 은닉 가능
인터페이스를 공개 가능
객체
클래스로부터 생성된 객체
→ (메모리에 올라간 객체는 인스턴스로 구분하여 명명하는 경우도 있다)
객체는 개별적으로 관리되며, 원하는 만큼 생성 가능하다.
객체를 통해 클래스에 정의된 멤버 함수를 호출 가능
개념적인 예
→ ”철수”는 학생이라는 클래스의 객체
→ ”영희”도 학생이라는 클래스의 객체
→ 철수와 영희는 각각 학번, 키, 나이등의 멤버 변수(데이터)를 갖고 있다.
클래스와 객체
클래스는 자료형(ex : int) 처럼 사용함
클래스는 새로운 데이터 타입을 만드는 것
int highScore;
int lowScore;
Player Kim;
Player Lee;
클래스의 정의 (Class Definition)
기본 모양
class ClassName{
//declarations;
};
예시
class Player{
//attributes
std:string name;
int helth;
int xp; // <- 멤버 변수, 데이터들
//method
void Talk(std::string text);
bool IsDead(); // <- 멤버 함수, 행위 /기능
};
객체의 생성
Player khk;
Player hero;
Player *enemy = new Player();
delete enemy;
계좌 클래스
class Account{
std::string name;
double balance;
bool Withdraw(double amount); // 출금
bool Deposit(double amount); // 입금
};
Account kimAccount;
Account leeAccount;
Account *parkAccount = new Account();
delete parkAccount;
클래스 멤버의 접근 (Accessing Class Members)
멤버 변수
멤버 함수
어떠한 멤버 변수 / 함수는 접근이 불가능 할 수있음(정보 은닉)
멤버 변수/ 함수에 접근하기 위해서는 객체가 필요
→ (Static 멤버의 경우는 예외)
클래스 객체인 경우
’.’ 연산자 사용 (멤버 접근 연산자)
Player player1;
player1.name;
player1.Move(2, 3);
객체의 포인터인 경우
역참조 후 점 연산자 사용
Account *kim_account = new Account();
(*kim_account).balance;
(*kim_account).Deposit(1000.00);
화살표 연산자(member of pointer 연산자) 사용
Account *kim_account = new Account();
kim_account -> balance;
kim_account -> Deposit(1000.00);
// (*ptr).balance 는 ptr -> balance 같다.
Class Member Access Modifier (visibility)
정보 은닉을 위해 멤버 접근을 제한할 수 있음
클래스 멤버 접근 제한자
public
→ 어디서든 접근 가능
private
→ 클래스의 멤버, friend 클래스에서만 접근 가능
protected
→상속된 클래스의 객체에서만 접근 가능
class Player{
public:
void Talk(std::string text);
bool IsDead();
private:
int health;
int xp;
};
클래스 멤버 접근 제한자의 적용
private 멤버에 접근하기 위해서는 public 멤버 함수가 필요
전부 public으로 할 경우
→ 멤버에 직접 접근하는 것이 오류를 초래할 수 있다.
→ 게임 플레이어 체력 100이 아닌 1000 할당도 가능
Player kim;
kim.name = "KQW" //compiler error;
kim.health = 1000; //error;
kim.Talk("Ready"); // ok
Player *enemy = new Player();
enemy -> xp = 100; // error;
enemy -> Talk("Read"); //ok
delete enemy;
계좌 객체 예시
계좌 잔액(balance)에 문제가 있다면? 입금(deposit)만 살펴보면 된다.
→ 잔액에 직접 접근이 불가능하고, public 입금 멤버함수를 통해서만 접근이 가능하기 때문
테스트 및 디버깅이 쉬워진다.
Account kimAccount;
kim_account.balance = 1000.0; //compiler error
kim_account.Deposit(1000.00); //ok
kim_account.name = "Kim's Account"; //error
Account *leeAccount = new Account();
lee_account -> balance = 1000.00; //error
lee_account -> Withdraw(1000.00); //ok
delete leeAccount;
멤버 함수의 구현 (Implementing Member Methods)
기존 함수의 구현과 유사
멤버 변수에 접근이 가능하기 때문에 인자로 전달할 데이터가 적어짐
클래스 선언 내에 구현 가능
→ Inline 구현
클래스 선언 외부에서도 구현 가능
→ Class_name::method_name
명세(specification)와 구현의 분리
→ 클래스의 선언은 .h 파일에 작성
→ 클래스의 구현은 .cpp파일에 작성
클래스 선언 내에 구현
class Account{
public:
void SetBalance(double bal){
balance = bal;
}
double GetBalance(){
return balance;
}
private:
double balance;
};
클래스 선언 외부에 구현
class Account{
public:
void SetBalance(double bal);
double GetBalance();
private:
double balance();
};
void Account::SetBalance(double bal){
balance = bal;
}
double Account::GetBalance(){
return balance;
}
명세와 구현의 분리
헤더 파일(.h)
→ include guard를 통해 전처리기에서 중복적인 헤더 파일의 선언을 방지
파일이름은 주로 클래스 이름과 동일
#infdef _ACCOUNT_H_
#define _ACCOUNT_H_
class Account
{
public:
void SetBalance(double bal);
double GetBalance();
private:
double balance;
};
#endif // <- include guard
구조체와 클래스 Struct vs Class
c++에서는 구조체와 클래스 모두 사용 가능
문법적으로는, 기본 접근 권한의 차이 외에는 차이점이 존재하지 않음
→ 클래스 : 명시되어 있지 않으면 private가 기본값
→ 구조체 : 명시되어 있지 않으면 public이 기본값
class Person{
std::string name;
std::string GetName();
};
Person p;
p.name = "Kim"; //error
cout << p.GetName(); // error
struct Person{
std::string name;
std::string GetName();
};
Person p;
p.name = "Kim"; // ok;
cout << p.GetName(); // ok
구조체와 클래스 사용 가이드라인
구조체
→ Public 접근이 필요한 데이터로 사용
→ 멤버 함수를 구조체 안에 설정하지 않는 것을 권고
클래스
→ Private 멤버 변수와 멤버 함수
→ 멤버 함수를 통해서 멤버 변수에 접근하도록 get/set 구현