🧠
Hi, Daehuyn Lee
  • Fork-my-brain
  • Network
    • 7. "데이터가 전달되는 원리" OSI 7계층 모델과 TCP:IP 모델
    • [Netwhat] 연습문제 정리
    • 11. IP 라우팅(routing) 동작 과정
    • 3. IP address 란?
    • 2. 컴퓨터 구조를 통해 이해하는 파일(File)과 소켓(Socket)
    • 10 "더 편리한 인터넷을 위해" DHCP && DNS 프로토콜
    • 9. 데이터? 세그먼트? 패킷? 헷갈릴 땐 PDU를 알아보자
    • 8. TCP 와 UDP 차이를 자세히 알아보자
    • 5. 서브넷팅(subnetting)으로 네크워크를 효율적으로 관리하자
    • 4. 넷마스크(Netmask)와 서브넷마스크(Subnetmask)
    • 1. 비유로 이해하는 컴퓨터 구조
    • 6. 공인(Public) && 사설(Private) IP의 차이점
  • Django
    • [Django 1] 가상환경에 Django 설치하기
    • [Django 3] Hello World 웹사이트 만들기
    • [Django 9] static 으로 css 로드하기
    • [Django 10] 한 템플릿에서 복수의 css 파일 적용하기
    • [Django 11] URL app별로 관리하기
    • [Django 8] 템플릿 상속
    • [Django 4] MTV 패턴
    • [Django 6] 블로그 model 만들기
    • [Django 2] Django는 어떻게 작동할까
    • [Django 7] '새 글 작성' 기능 만들기
    • [Django 5] 템플릿 언어
  • Projects
    • 예발자닷컴
      • 4. 프론트엔드의 역할은 어디까지 - 더미데이터 만들기
      • 7. [React 리팩토링] CSS Inline Styling에 Props 사용하기
      • 6. [React 리팩토링] JSX에서 조건문 사용해 렌더링하기
      • 3. 예발자닷컴 프론트서버 업데이트 하기
      • 8. [React 리팩토링] 예발자 프로젝트에 Redux 적용하기
      • 5. [React 리팩토링] JSX로 HTML 렌더링하기
      • 1. 👨‍👨‍👦‍👦 Github로 협업 프로젝트 관리하기
      • 2. React Component를 활용한 웹페이지 디자인 연습
  • Git
    • [Git] Interactive Rebase 실습
    • 오픈소스 개발 참여에 필요한 Git 명령어 정리
    • 개발자가 오픈소스를 읽는 방법
    • 오픈소스 프로젝트 시작하기
    • SSH agent ; Passphrase 입력 없이 Push하기
    • SSH로 원격저장소 접속하기
    • [Github] 개인 저장소를 팀 저장소로 변경하기
    • GitHub Dependabot
    • Git add, commit, push 취소하기
    • 깃헙 잔디 관리 팁
    • 원격저장소 여러개 연결하기
    • Typora(마크다운 에디터) 사용법
  • C
    • C Piscine
      • 메모리 구조를 알아보자
      • Makefile 만들기
      • GCC로 정적 라이브러리 파일 만들기
      • 외부 라이브러리 GCC로 컴파일 하기
      • 정적(Static) 변수
      • 저수준 파일 입출력
      • Makefile 자주 사용하는 문법 정리
      • segmentation fault 해결하기
      • C의 구조체 개념
      • 연결 리스트(linked list)에서 이중 포인터 사용하기
      • 로컬에 Norminette 설치하기
    • GetNextLine
      • [GetNextLine] 과제소개-Reading a line on a fd is way too tedious
      • [GetNextLine] 삽질의 기록
      • [GetNextLine] 리팩토링-프로그램의 목적을 고려한 코드
    • ft_printf
      • 1. 과제소개
      • 2. 가변인자 (Variadic Arguments)
      • 3. 형식태그와 서식지정자 printf 함수의 옵션 알아보기
    • Libft
      • [Libft] Bonus
      • [Libft] Test Program
      • [Libft] 나만의 C 라이브러리 만들기
      • [Libft] Part 2
      • [Libft] Part 1
  • UNIX shell
    • [minishell] 4. 종료상태와 에러메세지 처리
    • [minishell] 1. 과제소개 및 선행지식
    • [minishell] 2. 프로그램 구조 및 개발 기록들
    • [minishell] 5. 파이프(Pipe) 처리
    • [minishell] 3. 시그널(Signal) 처리하기
    • [minishell] 6. 리다이렉션(Redirection) 처리
  • Web
    • Next.js
      • [Next.js] CSS모듈과 복수의 class 사용하기
    • Node.js
      • [Node.js] 웹페이지에 파일 띄우기
      • [Node.js] URL에서 쿼리스트링 추출하기
      • [Node.js] '새 글 작성' 페이지 만들기
    • React
      • [React] 2. 컴포넌트(Component) 생성 및 파일별로 분리하기
      • [React] 1. 파일 구조 이해하기
      • [React] 4. 컴포넌트의 State 란
      • [React] 3. 컴포넌트의 Props 란
    • Javascript
      • Click, Enter 두 개의 이벤트 동시에 등록하기
      • Click eventListener 등록하기
      • JavaScript & C 문법 비교
      • JavaScript 객체 지향의 특징
    • CSS
      • [CSS] box-model, display, position
  • Docker
    • ft_server
      • 2. 도커 설치부터 워드프레스 구축까지
      • 1. 선행지식-Docker? Debian Buster? Nginx? ...
      • 3. Dockerfile 만들기
  • Kubernetes
    • 🌌[쿠버네티스 아키텍처] 3. API 호출
    • 🌌[쿠버네티스 아키텍처] 1. 구성 및 설계
    • 🌌[쿠버네티스 아키텍처] 2. 오브젝트 (Objects)
  • Operating System
    • Philosophers
      • [Philosophers] 예시예제로 보는 뮤텍스와 세마포어의 차이
      • [Philosophers] 식사하는 철학자 문제 소개
  • CPP
    • [CPP-08] STL containers, iterators, algorithms
    • [CPP-06] CPP 형변환 연산자
    • [CPP-04 ex02] 인터페이스(Interface) 클래스
    • [CPP-04 ex00] 다형성(Polymorphism) 및 가상함수
    • [CPP-02] Canonical 클래스 복사 생성자와 대입 연산자 오버로딩
    • [CPP-07] Templates
    • [CPP-01] this 포인터와 문자열 스트림(stringstream)
    • [CPP-01] 클래스의 정적할당과 동적할당 new, delete
    • [CPP-01] 파일 입출력 및 문자열 치환하기
    • [CPP-01] 참조자(reference)와 포인터는 다르다
    • [CPP-02] 정수부동소수값 - 고정소수값 변환
    • [CPP-04 ex01] 추상 클래스의 필요성 순수 가상함수
    • [CPP-00] Megaphone! CPP 표준입출력
    • [CPP-03] (ClapTrap이 뭐지) 다중 상속과 가상 상속
    • [CPP-05] 예외 처리 (exception handling)
    • [CPP-00] 객체지향의 관점으로 클래스 이해하기
    • [CPP-01] 랜덤값 얻기
  • IBM Cloud
    • [IBM Cloud] 1. 클라우드 컴퓨팅 개요
    • [IBM Cloud] 5. 클라우드 컴퓨팅의 구성 요소
    • [IBM Cloud] 3. 클라우드 서비스 모델 및 배포 모델
    • [IBM Cloud] 2. 클라우드를 활용하는 새 기술들
    • [IBM Cloud] 4. 떠오르는 클라우드 트렌드
    • [IBM Cloud] 6. 클라우드 스토리지 유형 및 CDN
  • Assembly
    • [libasm] 어셈블리 프로그램 구조와 x64 레지스터 이해하기
    • [libasm] strlen 함수를 어셈블리어로 짠다면
    • [libasm] 어셈블리 명령어(opcode) 정리
Powered by GitBook
On this page
  • 고민의 지점들
  • 1. 클래스의 기본 개념
  • 구조체와 클래스의 차이
  • 2. 객체지향 프로그래밍의 이해
  • 2.1. 객체를 이루는 것은 데이터와 기능
  • 2.2. 객체와 인스턴스
  • 2.3. 객체지향 프로그래밍의 특징
  • 3. 접근 제어
  • 3.1. 접근 제어가 왜 필요할까? 정보은닉과 캡슐화
  • 4. 멤버함수 정의 방법
  • 5. 생성자와 소멸자
  • 5.1. 멤버 이니셜라이저 (member Initializer)
  • 5.2. 소멸자
  • 6. 클래스 외부에서의 멤버변수 접근
  • 6.1. 엑세스 함수
  • 6.2. const 함수

Was this helpful?

  1. CPP

[CPP-00] 객체지향의 관점으로 클래스 이해하기

My Awesome Phonebook 과제를 통해 객체지향으로 프로그램을 설계하는 방법, 생성자로 인스턴스를 생성하는 방법, 접근제어 지시자와 const 함수로 좋은 클래스를 정의하는 방법을 학습했다.

[CPP Module00 / EX01: My Awesome Phonebook 과제 소개]

The user is prompted for input: you should accept the ADD command, the SEARCH command or the EXIT command. Any other input is discarded

  • 프로그램은 연락처가 없는 비어있는 상태에서 시작되며, 동적할당은 허용되지 않는다.

  • 최대 8개의 연락처를 저장할 수 있다. 아홉 번째 번호를 저장하려고 할 때 행동을 정의해야한다.

  • 명령이 올바르게 실행되었다면, 다시 프로그램은 ADD or SEARCH or EXIT 명령의 입력을 기다린다.

  • EXIT command

    • 프로그램은 종료되며 연락처 정보는 영원히 사라진다.

  • ADD command

    • 새 연락처의 모든 필드가 채워질 때 까지 사용자에게 정보를 받는다.

    • 연락처는 다음 11개의 필드로 구성된다.

      1. first name

      2. last name

      3. nickname

      4. login

      5. postal address

      6. email address

      7. phone number

      8. birthday date

      9. favorite meal

      10. underwear color (?)

      11. darkest secret

    • 연락처는 반드시 클래스의 인스턴스로 만들어져야한다.

  • SEARCH command

    • 모든 연락처의 리스트를 출력한다. 디스플레이는 다음 4개의 열(column)으로 구성되어있다.

      • index, first name, last name, nickname

    • 각 열의 너비는 최대 10글자이며, 그 이상의 문자는 자른 뒤 10번째 출력을 dot(.)으로 대체한다. 오른쪽 정렬한다.

    • 그 후, 사용자에게 특정 인덱스 연락처를 묻는 메시지를 표시하고, 입력을 받으면 해당 인덱스의 연락처 필드를 한 줄에 하나씩 출력한다. (총 11줄)

    • 입력이 올바르지 않을 때의 동작을 정의해야한다.

고민의 지점들

  • CPP에서는 파일을 어떻게 나누는지?

  • CPP 변수/함수명 컨벤션?

  • 객체지향 프로그래밍, 클래스, 인스턴스가 뭔지?

  • 객체를 어떻게 생성하고 데이터를 변경하는지?

1. 클래스의 기본 개념

구조체와 클래스의 차이

C의 구조체를 떠올리며 클래스를 이해해보자. CPP의 구조체는 C와는 다르게 구조체 멤버로 함수를 포함할 수 있다. 클래스와 구조체는 유사한 개념이다. 클래스는 자료 저장(변수) + 자료 처리(함수) 가 가능한 자료형이고, 특정 용도를 수행하기 위한 변수와 함수를 모아 둔 틀이라고 말할 수 있다.

CPP의 클래스는 구조체 개념에 딱 한 가지, 접근 제어라는 개념을 추가했다. 그렇다고 클래스를 그저 구조체의 확장 버전이라고 말하기엔 부족하다. CPP은 기본적으로 객체지향 언어이기 때문이다. 객체지향의 관점에서 클래스를 이해해야 한다.

2. 객체지향 프로그래밍의 이해

CPP에서 말하는 Object의 의미는 우리 주변에 존재하는 사물, 또는 대상이다. 따라서 객체지향 프로그래밍은 현실에 존재하는 사물과 대상, 그리고 그들의 행동을 있는 그대로 실체화 시키는 형태의 프로그래밍이다. 그리고 이때 클래스는 객체를 만들어 내기 위한 틀로서 사용되는 개념이다.

2.1. 객체를 이루는 것은 데이터와 기능

42 CPP Module00의 My Awesome Phonebook 과제를 예로 들어보자. 이 과제에서는 연락처를 추가하고 조회하는 프로그램을 만들어야 한다. 이 프로그램에는 전화번호부 객체가 존재할 것이다. 그리고, 아래와 같은 일들을 할 것이다.

  1. 전화번호부에 연락처를 추가하거나 조회한다.

  2. 전화번호부에는 총 8명의 사람이 저장되어 있다.

  3. 전화번호부는 각 사람의 이름, 전화번호를 저장하고 있다.

1은 전화번호부의 행동(behavior)을 의미한다. 그리고 2와 3은 전화번호부의 상태(state)를 의미한다.

이처럼 객체는 하나 이상의 상태 데이터와 하나 이상의 행동(기능)으로 구성된다. 상태 정보는 변수(속성)를 통해, 행동은 함수(메서드)를 통해 구현한다.

class Contact {
private:
    std::string first_name;
    std::string last_name;
    std::string phone_number;


public:
    void set_first_name(const std::string str);
    void set_last_name(const std::string str);
    void set_phone_number(const std::string str);

    std::string get_first_name(void) const;
    std::string get_last_name(void) const;
    std::string get_phone_number(void) const;
};

위와 같은 모습의 클래스를 선언했다.

2.2. 객체와 인스턴스

즉, 메모리에 올라간 특정 클래스 타입의 객체를 인스턴스(instance) 라고 부른다. 하지만 개념적으로 인스턴스는 객체에 포함되므로 두 단어가 같은 뜻인 것처럼 흔히 사용되는 것 같다. 정확히 구분해서 사용하면 좋을 듯.

인스턴스의 특징

  • 인스턴스는 독립된 메모리 공간에 저장된 자신만의 멤버 변수를 가진다.

  • 하지만 멤버 함수는 모든 인스턴스가 공유한다.

2.3. 객체지향 프로그래밍의 특징

  1. 추상화(abstraction)

  2. 캡슐화(encapsulation)

  3. 정보 은닉(data hiding)

  4. 상속성(inheritance)

  5. 다형성(polymorphism)

3. 접근 제어

CPP에서는 객체 지향 프로그래밍의 기본 규칙인 정보 은닉 을 위해 접근 제어(access control)라는 기능을 제공한다. 아래 3개의 접근 제어 지시자를 통해, 클래스 내부의 멤버에 대한 외부에서의 직접적인 접근을 허용하거나 차단할 수 있다.

  • public : 멤버에 관한 모든 외부 접근이 허용

  • private : 모든 접근 차단

  • protected : 상속 관계에 있는 파생 클래스에서의 접근만 허용

참고로 클래스의 기본 접근 제어 권한은 private이며, 구조체 및 공용체는 public 이다.

3.1. 접근 제어가 왜 필요할까? 정보은닉과 캡슐화

#include <iostream>

using namespace std;

struct TV {
  bool powerOn;
  int channel;
  int volume;

  void setVolume(int vol) {
    if (vol >= 0 && vol <= 100) 
      volume = vol;
  }
};

int main() {
  TV lg;
  lg.powerOn = true;
  lg.channel = 11;
  lg.setVolume(50);
  lg.volume = 400; // 의도치 않은 접근 가능
}

TV 구조체의 각 멤버를 정의하고 메인 함수에서 초기화를 해주고 있다. 만약 개발자가 볼륨의 크기를 0부터 100으로 제한하고 싶다면, 위 예제처럼 구조체 내부에 setVolume이라는 함수를 만들어 사용하도록 할 수 있을 것이다.

하지만 위 코드의 문제점은, 여전히 volume 멤버에 대한 접근이 가능하다는 점이다. 그래서 나온 CPP의 해결책이 접근 지시자이다. 아래 코드 처럼 접근 지시자를 이용해 정보 은닉을 실현하고, 보다 안정적인 프로그램을 개발할 수 있다.

#include <iostream>

using namespace std;

struct TV {
private: //외부 접근 차단
  bool powerOn;
  int channel;
  int volume;

public: //외부 접근 허용
  void on() {
    powerOn = true;
    cout << "TV on." << endl;
  }

  void off() {
    powerOn = false;
    cout << "TV off." << endl;
  }

  void setChannel(int num) {
    if (num >= 1 && num <= 999)
      channel = num;
       cout << "채널을 " << num << "(으)로 변경했습니다." << endl;
  }

  void setVolume(int vol) {
    if (vol >= 0 && vol <= 100) 
      volume = vol;
    cout << "볼륨을 " << vol << "(으)로 변경했습니다." << endl;
  }
};

int main() {
  TV lg;
  lg.powerOn = on();
  lg.setChannel(11);
  lg.setVolume(50);
}

멤버변수를 private로 선언하고, 해당 변수에 접근하는 함수를 별도로 정의해서, 안전한 형태로 멤버변수의 접근을 유도하는 것이 바로 정보은닉이며, 이는 좋은 클래스가 되기 위한 기본조건이 된다. 이렇게 어떤 멤버는 보호를 하고, 외부 사용자들에게 접근할 수 있는 인터페이스를 따로 만들어 주는 것을 '캡슐화'라고 부른다.

4. 멤버함수 정의 방법

멤버함수를 정의하는 방법은 두 가지가 있다.

  • 내부 클래스 정의

    • 클래스 내부에서 함수 정의

  • 외부 클래스 정의

    • :: 연산자 (scope resolution)를 사용

예제 코드

#include <iostream>

using namespace std;

class TV {
private:
  bool powerOn;
  int volume;

public: 
  // 내부 클래스 정의
  void on() {
    powerOn = true;
    cout << "TV on." << endl;
  }

  void off() {
    powerOn = false;
    cout << "TV off." << endl;
  }

  // 외부 클래스 정의
  void setVolume(int vol);
};

// 외부 클래스 정의
void TV::setVolume(int vol) {
  if (vol >= 0 && vol <= 100) 
    volume = vol;
  cout << "볼륨을 " << vol << "(으)로 변경했습니다." << endl;
}

int main() {
  TV lg;

  lg.powerOn = on();
  lg.setVolume(50);
}

5. 생성자와 소멸자

인스턴스는 모든 멤버 변수를 초기화 하기 전까지는 사용할 수 없다. private 멤버는 일반적인 방식으로 초기화할 수 없기 때문에, CPP에서는 생성자(constructor) 라는 멤버 함수를 제공한다. 클래스에서 객체를 생성할 때마다 해당 클래스의 생성자가 컴파일러에 의해 자동으로 호출된다.

  • 생성자는 객체의 생성과 동시에 멤버 변수를 초기화해주는 pubilc 함수이다.

  • 클래스 생성자의 이름은 해당 클래스의 이름과 같아야 한다.

  • 생성자는 초기화를 위한 데이터를 인수로 전달받을 수 있다.

  • 클래스 생성자의 원형은 클래스 선언의 public 영역에 포함되어야 한다.

  • 반환값이 없지만 void형으로 선언하지 않는다.

  • 한 클래스는 여러 개의 생성자를 가질 수 있다.

예제 코드

#include <iostream>

using namespace std;

class TV {
private:
  bool powerOn;
  int volume;

public: 
    TV(void); // 생성자 함수의 원형
}

// 생성자 함수
TV::TV(void){
  powerOn = true;
  volume = 50;
}

int main() {
  TV lg; // 생성자의 암시적 호출
  // TV lg = TV // 생성자의 명시적 호출
}

5.1. 멤버 이니셜라이저 (member Initializer)

멤버 이니셜라이저는 객체 혹은 멤버변수의 초기화를 보다 간단하게 만들어준다. 아래 예제가 이니셜라이저를 사용한 예제이다. :(콜론) 기호 뒤의 volume(50) 이 volume을 50으로 초기화하라는 뜻이다.

class TV {
private:
  bool powerOn;
  int volume;

public: 
    TV(void) : volume(50) {} //이니셜라이저를 통한 초기화
}

따라서, 객체를 생성할 때는 생성자 함수 몸체에서 초기화하는 것과 이니셜라이저를 이용하는 것 두 가지 방법이 존재한다. 일반적으로 멤버변수 초기화를 할때는 이니셜라이저를 선호한다고 한다. 선언과 동시에 초기화가 이루어지니 대입연산보다 성능 상의 이점이 있는 것 같다.

  • 초기화할 멤버가 여러개면 ,(콤마) 기호로 나열하면 된다.

  • const 멤버변수도 이니셜라이저를 이용하면 초기화가 가능하다.

5.2. 소멸자

생성자와 반대로 객체 소멸시 반드시 호출되는 것이 소멸자이다.

  • 클래스의 이름앞에 ~ 기호가 붙는 형태의 이름을 갖는다.

  • 반환형이 선언되어 있지 않고, 실제로 반환되지 않는다.

  • 소멸자는 객체 소멸과정에서 자동으로 호출된다. 또한 직접 소멸자를 정의하지 않으면, 디폴트 생성자와 마찬가지로 자동 삽입된다.

  • 만약 생성자 내에 new 연산자를 이용해 할당해 놓은 메모리가 있다면 delete 연산자를 이용해 소멸자에서 소멸해야 한다.

6. 클래스 외부에서의 멤버변수 접근

class Contact {
private:
    std::string first_name;
    std::string last_name;
    std::string phone_number;

public:
    void set_first_name(const std::string str);
    void set_last_name(const std::string str);
    void set_phone_number(const std::string str);

    std::string get_first_name(void) const;
    std::string get_last_name(void) const;
    std::string get_phone_number(void) const;
};

6.1. 엑세스 함수

My Awesome Phonebook 과제 예제를 다시 가져와 보면, 멤버함수들에는 set, get과 같은 이름이 붙어있는 걸 알 수 있다. CPP에서는 이런 함수명을 자주 쓰는데, 흔히 엑세스 함수 라고 부른다. 이들은 멤버변수를 private로 선언하면서 클래스 외부에서의 멤버변수 접근을 목적으로 정의되는 함수들이다.

6.2. const 함수

그리고 getter함수들 뒤에 const 지시자가 붙어 있는 걸 알 수 있다. const는 이 함수 내에서는 멤버변수에 저장된 값을 변경하지 않겠다 라는 선언이다. 따라서 const 선언이 추가된 멤버함수 내에서 멤버변수의 값을 변경한다면 컴파일 에러가 발생한다. const 함수 내에서는 const가 아닌 함수의 호출이 제한된다. 아예 멤버변수의 값이 변경될 수 있을 일말의 가능성조차 허용하지 않기 위해서다.

사실 처음에는 CPP의 클래스와 객체지향 프로그래밍 개념을 학습하면서 C에 비해 개발의 자유도가 제한되고 너무 복잡한 기능들이 추가된 것 아닌가 생각했었다. 그런데 바로 그게 이 모든 새 기능들이 추가된 이유인 것 같다. 제한된 방법으로의 접근만 허용을 해서 잘못된 값이 저장되지 않도록 도와주는 것이고, 또 실수를 했을 때 실수가 쉽게 발견되도록 해야 한다는 일관된 원칙이 느껴져서 설계 자체에 재미를 갖게 됐다.

Previous[CPP-05] 예외 처리 (exception handling)Next[CPP-01] 랜덤값 얻기

Last updated 3 years ago

Was this helpful?

객체와 인스턴스라는 말이 혼용되어 사용되는 느낌이 들어 헷갈렸다. 정확한 개념을 찾아보니 객체(Object)는 소프트웨어 세계에 구현할 대상이고, 이를 구현하기 위한 설계도가 클래스(Class)이며, 이 설계도에 따라 소프트웨어 세계에 구현된 실체가 인스턴스(Instance)이다.

우측정렬 방법?
입력버퍼를 비우는방법?
출처