3주차 일기

이번 한주는 많은 일들이 VEDA에 찾아왔습니다. 첫 번째 교과평가와 함께 회식을 진행했습니다...! 😇

그러면 또 치열했던 한 주간의 기록 시작해보겠읍니다.

3주차 교육 기록

우선은 3주차 교육 내용부터 피드백을 해보자구요.
지난 2주차에 이어 C++ 언어를 학습하며 이번 주에는 다형성,상속성을 위주로 객체 지향 언어에 대해 배웠습니다.

상속성이란?

이미 추상화, 캡슐화 된 클래스를 확장하고 변형하는 방법입니다.

자식 클래스는 부모 클래스를 대체할 수 있다. [리스코프 치환 법칙]

이런 상속성에는 여러 가지 조건이 있습니다.
1. 부모가 외부로 노출한 함수의 시그니처를 그대로 상속해야한다.
→ 부모 클래스의 속성과 기능을 변경하면 안됨.

  1. 어떤 위치에서도 부모의 클래스를 대체할 수 있어야한다.

    ❓시그니처 :  함수의 이름, 매개변수 구성, 반환 형식을 아우르는 표현

여기서 가상 함수라는 개념이 등장을 합니다.

virutal 키워드 ?
⇒ 상속 받는 함수의 기능이 달라진다.

  • 가상 함수를 이용한 오버라이딩 ⇒ 다형성 구현

    일반 함수 호출 : runtime에서 실제 객체의 변수 타입을 보고 타입에 해당하는 함수를 호출하는 것

    runtime에서 객체끼리 형 변환 시 부모 클래스 포인터로 자식 클래스에 접근하면 부모 클래스 멤버 외에는 확인 불가능

    for (int i = 0; i < 2; i++) {
    	arr[i]->getArea();
    } 
    //일반 함수 호출로 인한 getArea()함수 실행 -> shape.getArea()함수 실행
    ⭐⭐⭐
    //virtual 함수 호출 
    => 객체의 함수를 호출한다  shape * arr[0]의 객체는 myTriptr;
    Triangle.getArea(); 실행

virtual func 의 사용

 //Shape.h
 
 class Shape
 {
 public:
 	Shape();
 	virtual int getArea(); //=> 객체의 자료형을 보고 함수 결정.
 	//int area;
 
 protected:
 	//외부에서 접근 가능하지만 데이터 보호 가능
 	int area;
 private:
 	//변수는  private에 둬야 안전한디?
 };
 
 //-----------------------------------
 //main.cpp
 int main(){
 						/*
 						.
 						.
 						.
 						*/
 	Shape* arr[2]; // shape 포인터  => 후손의 class를 넣을 수 있다.
 	arr[0] = myTriptr;  //부모의 포인터 자식의 포인터 
 	arr[1] = myRectPtr;
 	
 	cout << "상속 TEST start" << endl;
 	 //Shape의 메소스 실행 
 	//Shape class 에 virtual 을 써야 각 자식 클래스의 getArea 사용 가능
 	for (int i = 0; i < 2; i++) {
 			arr[i]->getArea(); //arr[i]에 할당된 객체의 getArea()함수를 실행
 	}
 }

이처럼 virtual func을 사용해 상속 관계에서 다형성을 이룰 수 있습니다.

  • 오버라이딩 : 상속 관계가 있고 부모 class와 함수의 시그니처, 함수명이 같은 함수가 있을 때 재정의 하는 것
  • 오버 로딩 : 이름만 같고 매개변수 구성이 다른 함수 → 호출 인자에 따라 알맞은 함수 호출.
    • 매개변수는 동일하고 리턴값이 다른 경우는 불가능

💡가상함수는 위와 같은 오버라이딩을 이용해서 자식 클래스가 부모 클래스로 업캐스팅 되었을 때 자식 클래스에서 오버라이딩 된 각각의 함수를 실행시켜 다형성을 구현시켜 주는 중요한 요소입니다.!!!!

이러한 특징들을 통해 변경 사항이 발생했을 때 소스 코드를 쉽고 빠르게 변경할 수 있습니다.

위의 두가지 개념이 3주차 VEDA 교육의 핵심 내용이라고 해도 과언이 아닐 정도로 주를 이루는 내용이니 다들 한번 짚고 넘어 가봅시다. ~~

동적 바인딩

가상 함수 키워드 virtual이 어떻게 구현되는지 알아봅시다.

가상함수 테이블을 통해 동적으로 바인딩

바인딩이란? 함수 호출이나 변수 참조가 해당 코드와 연결되는것.

  • 정적 바인딩 : 컴파일 당시 결정되어 실행 동안 유지 → 실행마다 주소는 바뀜

  • 동적 바인딩 : 대상(객체)가 실행되는 시점에 결정되며 변경 될 수 있음.

가상함수, 자식 클래스로 치환된 부모의 클래스 포인터가 동적으로 바인딩 된다.

  1. 정적 바인딩은 바로 함수의 주소로 이동
  2. 동적 바인딩은 클래스 객체로 이동한 뒤 객체에서 해당 함수의 주소로 이동

가상함수 테이블 ⇒ 동적 바인딩이 가능하게 함

가상 함수가 있는 클래스로 객체를 생성하면 메모리에 _vfptr이라는 가상 함수 테이블을 가리키는 포인터가 자동으로 생기고 이를 가상 함수 호출에 활용
- 최상위 클래스에만 존재한다
- 자식 클래스는 부모의 _vfptr을 상속 받으며 오버라이딩 할 때 해당 함수의 주소를 등록

==> 같은 함수를 호출하더라도 포인터가 가리키는 객체에 따라 호출되는 구현체가 다르다.

클래스 별로 테이블을 추가한다. 동일 함수 일때 가상함수 테이블에서 인덱스 값은 같다고 생각하면 됩니다.

함수 연산자 오버로딩

위에서 오버 로딩과 오버 라이딩의 차이를 확인했으니 이번에는 함수 연산자 오버로딩에 대해 알아봅시다.

제게는 살짝 어려운 개념이였어서 복습용! 으로 기록을 해두도록 하겠습니다.

약간 흔히 코딩에서 사용하는 연산자를 입맛대로 바꿀 수 있는 방식인데요. 잘 사용하면 유용할 거 같으니 이리저리 바꿔보며 익혀보도롭 합시다..🥸

operator 키워드를 사용해 객체끼리 연산할 수 있음.

오버로딩을 이용해서 더하기 연산자를 만들어 보자. ⇒ 같은 이름으로 매개변수를 추가해서 재정의.

반환_형식 operator연산자_기호(매개변수)

//함수 선언 
monster_c operator+(moster_c &operand);
//함수 정의
monster_c monster_c::operator+(monster_c &operand){
	monster_c result_monster; //새로운 객체 생성 -> level 더하기 수행 
	result_monster.set_level(level + operand.get_level());
	return result_monster;
}

위의 예시 코드대로 작성을 하게 되면 monster_c class 객체끼리 + 연산을 했을 때 특정 동작이 반복되도록 함수를 구현할 수 있습니다!

컴포지션과 어그리게이션

다중 상속을 활용하게 되면 다양한 문제들이 발생을 하는데요 이를 해결하기 위한 컴포지션과 어그리게이션이라는 개념이 존재합니다.

꼭꼭 씹어 먹어 봅시다.

  • 다중 상속의 문제

    • 클래스가 커지는 문제
      • 상속이나 의존도가 높아지면 결합도가 높아진다.
    • 컴파일 시간이 늘어나는 문제

    ⇒ 확장에 열려있고 수정에 닫혀 있다는 법칙에 위배.

이 두가지의 문제가 존재합니다.

컴포지션

분리한 클래스를 포함하는 개념 [part-of] 

클래스가 가져야 할 특징을 상속받는 것이 아니라 멤버 변수로 포함하는것

→ 재활용할 속성과 기능을 별도의 클래스로 구성하고 멤버로 해당 클래스의 객체를 갖는 것
⇒ 이는 생명주기를 함께 한다

상속과 차이점

  • 동적 바인딩(늦은 바인딩이 가능 : 호출 대상이 호출 시점에 결정 되는것)
  • 클래스가 변경될 때 멤버인 객체는 변경되지 않아도 된다.

어그리게이션

분리한 클래스를 사용하는 개념[has -a]

분리한 클래스의 객체를 포인터나 레퍼런스 변수로 포함한다
→ 이는 생명주기가 다름 : 클래스 인스턴스가 소멸되더라도 포인터 객체는 여전히 남아있음
컴포지션과는 달리 분리된 클래스가 이를 사용하는 클래스와 유연한 관계를 가진다.
⇒ 리스코프 치환 법칙에 따라 분리된 클래스 직접 참조 or 해당 클래스의 자식 클래스 참조 가능.

⭐단 레퍼런스 변수로 포함할 때는 생성자에서 초기화를 꼭 해야한다.

객체 지향 프로그래밍 원칙

1️⃣단일 책임 원칙(SRP)
클래스는 한가지 기능만 수행해야하고 한가지 이유로만 변경해야한다 는 원칙

변경된 클래스가 다른 클래스에 영향을 주지 않는 것

2️⃣OCP (개방 폐쇄 원칙)
확장에 열려있고 수정에 닫혀 있다.

  • 동적 바인딩이 해당 원칙을 잘 설명한다.

  • 새로운 기능은 언제든 추가 가능

    → 다른 코드에 파급 효과가 없어 추가되는 기능 외에는 수정이 필요 없다 ( closed)

  • 추상 클래스[인터페이스] 활용

    • 인터페이스 : 순수 가상함수만을 포함하는 추상 클래스
    • 템플릿을 만들고 내용은 하위 클래스에 위임 ⇒ 템플릿 메서드 패턴
    • 하위 클래스에서 순수 가상함수를 바꾸더라도 다른 코드에 영향 x

3️⃣LSP (리스코프 치환 원칙)

하위 클래스는 상위 클래스로 대체할 수 있어야 한다.

다형성의 동작 원리

  1. 부모의 클래스를 상속받아 구현한 자식 클래스는 부모 클래스로 업캐스팅이 가능하다.
  2. 자식 클래스에서 부모 클래스의 멤버 함수를 상속받아 오버라이딩하거나 유지해야 한다.

4️⃣ISP(인터페이스 분리 원칙)

인터페이스란 무엇인가? ⇒ 순수 가상함수만으로 구성된 추상 클래스

  • ISP
    인터페이스는 작고 섬세해야 하고 클래스는 필요한 인터페이스만 구현해야한다.
    • 인터페이스는 반드시 최소의 기능만으로 정의
    • 클래스는 최소한의 인터페이스만을 상속받아서 구현해야함
    • 여러 기능이 필요한 클래스라면 has-a 로 직접 구현이 아닌 멤버로 포함해야 함

5️⃣ DIP(의존성 역전 원칙)

상위 수준 모듈은 하위 수준의 모듈에 의존해서는 안 되며 상위·하위 수준 모두 추상 레이어(인터페이스)에 의존해야한다.

하위 클래스를 추가하거나 삭제할 때 상위 클래스를 수정해야하는 것 → 상위 수준 모듈이 하위 수준 모듈에 의존한다.

⇒ 이때 두 클래스 간에 인터페이스를 추가해 의존성을 끊어준다.

→ 인터페이스를 상속한 클래스가 무엇이든 함수가 변경되지 않음.


이것 외에도 템플릿이나 표준 라이브러리 와 같은 개념들을 배울 수 있었지만 이는 개념보다는 실사용을 통해서 몸에 익히는 게 중요하게 느껴졌습니다.
별도로 정리를 하지 않고 교과 평가에 대한 후기를 남기며 블로그를 마무리 하도록 하겠습니다...

교과 평가..

VEDA의 첫 교과평가를 남기고 2주간 책 2권 분량을 공부하며 시험을 준비하던 나.. 어느새 정신 차려보니 시험은 코앞으로 다가와있었다.

헷갈리던 포인터와 배열, 문자열 입력 처리를 위주로 공부하고 어려운 개념 위주로 코드 결과,문제를 반복해서 풀었습니다.
그래서 시험은 어땠냐구요???.

크게 어려운 문제는 없었습니다. 헷갈릴만한 문제는 없었지만 기초적인 개념위주로 C언어를 학습해왔다면 충분히 좋은 성적을 거둘 수 있었을 거라 생각합니다.

남은 평가들도 파이탱~~~ 하면서 VEDA 6회차를 이어나가 보도록 하겠습니다..


막간을 이용한 3주차 일기😼

3주차 맛집 소개.

두메
https://naver.me/xs3GnKA8

슴슴한 산나물 정식과 정갈한 밑반찬
다소 채식주의를 지향하는 느낌이 있지만 맛있었다.

평점 :⭐️⭐️⭐️ [5점 만점]

크라이 치즈버거 (양재역점)
https://naver.me/GkR3DGTq

새로 발견한 깔끔한 햄버거 집, 누군가에게는 인생 버거 집일지도. 하지만 나 최진철 햄버거는 누구보다 엄격하다.
평점 :⭐️⭐️⭐️+0.7 [5점 만점]

청담 돈가스
지나가는 길에 본 양념게장+돈가스 정식을 파는 레전드 가성비 집. 물론 양념게장을 맛본 것은 아니지만 다슬기탕에도 도전하는 미식가에게 인정받은 보장된 맛집이다. 자주 가게 될 것만 같은 식당.. 그런 느낌
평점 :⭐️⭐️⭐️⭐️+0.2 [5점 만점]
![청담 돈가스]알밥 정식

덧붙여.. 회식을 진행했다. 마음 같아서는 후다닥 먹고 돔황챠~ 를 시전하려고 했지만 정신 차려보니 11시반 .. 깨질 것 같은 머리를 부여잡고 집으로 돌어와씀..ㅎ

VEDA 6회차 파이탱~~~

그러면 더욱 성장해서 4주차 일지를 들고 오도록 하겠습니다.. Goog bye~

profile
세상의 어려운 문제를 해결하자

0개의 댓글