본문 바로가기

프로그래밍 공부/C++

C++ 6판 Chapter.10 객체와 클래스

객체와 클래스 


객체 지향 프로그래밍(OOP)의 중요 기능들은

  • 추상화
  • 캡슐화와 데이터 은닉
  • 다형
  • 상속 
  • 코드의 재활용

 가 있으며 클래스는 이러한 OOP 기능들을 구현하고 결합하는 데 사용되는 가장 중요한 C++도구이다.


1. 절차식 프로그래밍과 객체 기향 프로그래밍


OOP과 일반 절차식 프로그래밍의 차이점

절차식 프로그래밍 - 절차식 접근 방식을 사용하여, 처리해야 할 절차에 우선적으로 초점을 맞추고, 그다음 데이터를 표현할 방법을 찾는다.

OOP- 데이터에 우선적으로 초점을 맞추고, 데이터를 어떻게 표현할지, 그리고 어떻게 사용할것인지를 가장먼저 생각한다.



2. 추상화와 클래스

  • 데이터형이란 무엇인가?

그것을 대상으로 수행할수 있는 연산에 의해 정의되는것 

ex) 

int형 - 모든 산술연산을 수행할수있다 

포인터 - int형과 같은 크기의 메모리를 요구하고, 내부적으로는 int형으로 나타낸다. 그러나 int 형에 적용할 수 있는 연사을 허용하지 않는다. 그러므로 어떤 변수를 int형으로 선언하거나 float형을 지시하는 포인터로 선언한다고 할 때, 그러한 선언은 단지 메모리만 대입하는 것이 아니라 그 데이터형 으로 수행할 수 있는 연산까지도 함께 정의하는것이다. 쉽게말해서 다음과 같은 세 가지를 결정하는것이다.

  • 데이터 객체에 필요한 메모리의 크기를 결정한다.
  • 메모리에 있는 비트들을 어떻게 해석할 것인지 결정한다.
  • 데이터 객체를 사용하여 수행할 수 있는 연산이나 메서드를 결정한다.


  • C++의 클래스

클래스는 추상화를 사용자 정의 데이터형으로 변환해 주는 C++의 수단이다. 

클래스는 데이터 표현과 데이터를 조작하는 메서드들을 하나의 패키지 안에 결합한다.


클래스 설계를 서술하는 첫 번째 단계는 클래스 선언을 제공하는 것이다. 클래스 선언은 구조체 선언과 모양이 비슷하다.

클래스 선언은 private 부분과 public 부분을 가진다.

private 부분에 선언된 멤버들은 멤버 함수들을 통해서만 접근할 수 있다. 주로 데이터 맴버들이 private에 들어간다.

public 부분에 선언된 맴버들은 클래스 객체를 사용하여 프로그램이 직접 접근 할 수 있다. 주로 데이터 맴버들이 public에 들어간다.


ex)

class className

{

private:

data member declarations

public: 

member function prototypes

};


public 부분에 있는 내용은 설계의 추상화 부분인 public 인터페이스가 된다. private부분에 데이터를 넣어 캡슐화하는 것은 데이터의 

무결성을 보호하기 위한 것으로, 데이터 은닉이라고 한다. 따라서 클래스의 사용은 추상화, 데이터 은닉, 캡슐화라는 OOP의 목표를 쉽게 구현할수 있게 해 주는 C++의 방법이다.

클래스 설계를 서술하는 두 번째 단계는 클래스 맴버 함수를 구현하는 것이다.

클래스 선언에 함수 원형 대신에 완전한 함수 정의를 넣을 수도 있다. 그러나 일반적으로

매우 짧은 함수들은 제외하고, 함수 정의들을 따로 분리하여 사용한다. 


3. 클래스 생성자와 파괴자


클래스를 사용할때 일반적으로 제공해야 하는 생성자와 파괴자라는 표준 함수가 있다. 

C++가 내세우는 목표 중의 하나는, 클래스 객체를 표준 데이터형을 사용하듯이 사용할 수 있게 만드는것이다.

그러나 stock형에 대해서는 일반적인 초기화 문법이 적용되지 않는다.

그 이유는 데이터들이 private접근 제어를 가지고 있어 프로그램이 데이터 맴버에 직접 접근할 수 없기 때문이다.

그러므로 객체를 성공적으로 초기화하려면 적당한 맴버 함수를 하나 고안할 필요가 있다.

일반적으로, 모든 객체는 그것을 생성할 때 초기화하는 것이 가장 바람직하다.

이를 위해서 C++는 새로운 객체를 생성하고 그들의 맴버에 값을 대입해 주는 클래스 생성자라는 특별한 맴버 함수를 제공한다.

  • 생성자의 선언과 정의

Stock 객체는 외부에서 제공되는 세 개의 값을 가지기 때문에, 그 생성자에 세 개의 매개변수를 제공해야 한다.


  • 생성자 사용하기

C++는 생성자를 이용하여 객체를 초기화하는 두 가지 방법을 제공한다. 

첫번째는 생성자를 명시적으로 호출하는 것이고, 두번째 방법은 생성자를 암시적으로 호출하는 것이다.

생성자는 다른 클래스 매서드들과 조금 다른 방식으로 사용된다. 일반적으로 우리는 메서드를 호출할 때 객체를 사용한다.

stock.show();  // stock1 객체가 show() 메서드를 호출한다

그러나 생성자가 객체를 만드는 일을 끝내기 전까지는 객체가 없는 상태이므로, 생성자 호출에 객체를 사용할 수 없다. 생성자는 객체에 의해 호출되는 것이 아니라, 객체를 생성하는 데 사용된다.


  • 디폴트 생성자
디폴트 생성자는 명시적인 초기화 값을 제공하지 않을 때 객체를 생성하는 데 사용하는 생성자이다.  
사용자가 어떠한 생성자도 정의하지 않을 경우에만 컴파일러가 디폴트 생성자를 제공한다.
명시적인 초기화를 하지 않고 객체를 생성하고 싶을 경우에는, 사용자 자신의 디폴트 생성자를 정의해야 한다. 
매개변수는 두 가지 방법으로 정의할 수 있다.
첫 번쨰 방법은 기존의 생성자에 있는 모든 매개변수에 디폴트 값을 제공하는 것이다.
두 번째 방법은 함수 오버로딩을 사용하여 매개변수가 없는 또 하나의 생성자를 정의하는 것이다.
사용자는 하나의 디폴트 생성자만 가질 수 있다. 그러므로 두 가지 방법을 다 사용하여 두 개의 디폴트 생성자를 만들면 안 된다.
  • 파괴자

객체의 수명이 끝나는 시점에서, 프로그램은 파괴자라는 무서운 별칭을 가진 특별한 맴버 함수를 자동으로 호출한다.

파괴자는 어떤 흔적도 남기지 않고 깨끗하게 없애야 한다. 따라서 어떤 면에서 파괴자는 유용한 역할을 한다.

생성자와 마찬가지로, 파괴자도 특별한 이름을 가진다. 그것은 앞에 틸데(~)가 붙은 클래스 이름으로부터 만들어진다.

생성자와 마찬가지로 파괴자도 리턴값을 가질 수 없고 , 선언된 데이터형을 갖지 않는다. 그러나 파괴자는 매개변수를 가지면 안된다.


파괴자는 컴파일러가 호출을 결절하고, 일반적으로 사용자가 코드에 명시적으로 파괴자를 호출하면 안 된다.

정적 기억 공간의 클래스 객체를 생성한다면, 프로그램이 종료될 때 파괴자가 자동으로 호출된다. 자동 기억 공간의 클래스 객체를 생성한다면, 그 객체가 정의된 코드 블록을 프로그램이 벗어날 때 파괴자가 자동으로 호출된다.


4. 객체 들여다보기, this 포인터


this 포인터는 하나의 매서드가 두 개의 객체를 동시에 처리할 수 있게 한다.

(뭘 적야할지 이해가 잘 않되요ㅠㅠ)



5. 객체 배열


객체 배열은 표준 데이터형의 배열을 선언하는 것과 완전히 동일한 방법으로 선언한다.

명시적으로 초기화되지 않은 클래스 객체들을 생성할 때, 프로그램은 항상 디폴트클래스 생성자를 호출한다. 이선언은 그 클래스가 생성자를 전혀 정의하지 않거나,또는 이 경우와 같이, 하나의 명시적인 디폴트 생성자를 정의할것을 요구한다.

생성자를 사용하여 배열 원소들을 초기화할 수 있다. 그러한 경우에, 각각의 원소에 대해 생성자를 호출해야 한다.

클래스가 생성자를 하나 이상 가지고 있을때에는, 배열의 각 원소마다 서로 다른 생성자를 사용할 수 있다.



6. 클래스 사용 범위

C++클래스는 클래스 사용 범위라는 새로운 종류의 사용 범위를 도입한다.
클래스 사용 범위는 클래스 데이터 멤버들의 이름이나 클래스 맴버 함수들의 이름과 같이, 클래스 안에서 정의되는 이름들에 적용된다.
클래스 사용범위를 갖는 것들은 클래스 안에는 알려지지만 클래스 바깥에는 알려지지 않는다. 그래서
클래스 멤버들을 서로 다른 클래스에 같은 이름으로 선언해도 충돌하지 않는다.
  • 클래스 사용 범위 상수

클래스 사용 범위를 가진 기호 상수를 사용하는 것이 좋을 때가 있다. 예를 들어,

Stock 클래스 선언은 compay 배열의 크기를 지정하기 위해 리터럴 30을 사용한다.

그 상수 30이 모든 객체들에 대해 동일하기 때문에, 모든 객체들이 공유하는 하나의 상수로 만드는 것도 나쁘지 않다.

그래서 다음을 하나의 해결책으로 잘못 생각할 수도 있다.


class stock

{

private:

const int Months = 12;            // 상수를 선언한다? 실패한다

double costs[Months];

     .....


이것은 동작하지 않는다. 왜냐하면 클래스를 선언하는 것은 객체가 어떻게 생겼는자 서술하는 것이지, 그 객체를 생성하는 것은 아니기 때문이다. 따라서 값을 저장할 기억 공간은 객체가 생성될 때까지 마련되지 않는다. 그러나 지금 운하는 것과 동일한 효과를 내는 두 가지 방법이 있다.


첫째, 클래스 안에 열거체를 선언할 수 있다. 클래스 선언 안에 주어지는 열거체는 클래스 사용 범위를 가진다. 따라서 열거체를 사용함으로써 클래스 사용 범위를 가지는 기호 이름들을 정수 상수들에 제공할 수 있다.

최근에는 C++ 키워드 static을 사용하여 클래스 안에 상수를 정의하는 제2의 방법을 도입했다


  • 범위가 정해진 열거(C++11)

전통적인 열거는 몇 가지 문제점을 안고 있다. 그중 하나는,

두 개의 다른 enum 정의로부터 온 열거자는 충돌하한다는 것이다.

C++11에서는 새로운 열거자 형식을 제공하여 열거자에게 클래스 범위를 갖게 함으로써 이러한 문제를 해결한다.

또 다른 벙법으로, 클래스 대신에 struct 키워드를 사용할 수 있다.


7. 추상화 데이터형 


Stock 클래스는 매우 특수하다.

스택의 특성을 일반적이고 추상적인 방식으로 살펴보자, 먼저 스택은 여러 개의 항목을 저장한다. 그다음에, 스택은 다음과 같은 연산을 수행할 수 있는 특성을 가지고 있다

  • 빈 스택을 생성할 수 있다
  • 스택의 꼭대기에 항목을 추가할 수 있다
  • 스택의 꼭대기에서 항목을 삭제할 수 있다
  • 스택이 가득 차 있는지 검사할 수 있다
  • 스택이 비어 있는지 검사할 수 있다

public 멤버 함수들로 스택연산을 나타내는 인터페이스를 제공하는 클래스 선언과 이 서술을 대응시킬 수 있다.

private 부분은 데이터 저장 방법을 정해야 한다.


정리를디게 못해서 중요한 부분이 없을수도 있고 이해가 안되는 부분도 많을꺼 같네요 ㅠㅠ 다음번엔 더 열심히 해야겠어요


'프로그래밍 공부 > C++' 카테고리의 다른 글

c++ 정리시작  (0) 2017.09.01