객체지향 프로그래밍

김여울·2025년 6월 4일

내일배움캠프

목록 보기
14/139

오늘은 배열의 평균 구하기와 짝수와 홀수를 풀었다.

// 평균 구하기

#include <stdio.h>
#include <stdbool.h>   
#include <stdlib.h>

double solution(int arr[], size_t arr_len)  // size_t = unsigned int : 양의 정수 전용 타입
{
    int sum = 0;   // 합계 저장할 변수 0으로 초기화

    for (size_t i = 0; i < arr_len; i++)  // size_t 타입의 i 사용
    {
        sum += arr[i];  // 배열의 i번째 값을 누적해서 합산
    }

    return (double)sum / arr_len;  // 평균 계산

arr_len 은 배열의 길이 = 배열의 크기이므로 평균을 구할 땐 합계 / arr_len을 하면 된다.

// 짝수와 홀수

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

char* solution(int num) 
{
    if (num % 2 == 0)
    {
        return (char*)"Even";
    }
    else 
    {
        return (char*)"Odd";
    }    
}

// 삼항 연산자 사용
/*
char* solution(int num) 
{
    return (char*)(num % 2 == 0 ? "Even" : "Odd");
}
*/

if / else문으로 조건을 나눠 2로 나눌 때 나머지가 0이면 짝수, 0이 아니면 홀수를 반환하도록 풀었다. 그리고 C++ 문법 강의에서 들었던 삼항 연산자를 통해서도 풀 수 있을 것 같았다. 조건을 만족하면 A, 만족하지 못하면 B가 나오니 짝수, 홀수를 출력할 수 있다.

강의는 드디어 1주차를 다 들었다!

📎 강의 자료 + 필기

상속

1️⃣ 기본 클래스 vs 파생 클래스

구분설명
기본 클래스 (Base, 부모 클래스)기능을 정의해두는 클래스
다른 클래스가 공통적으로 사용할 기능을 정의
AAnimal, ACharacter
파생 클래스 (Derived, 자식 클래스)기본 클래스를 물려받는 클래스
기능을 그대로 쓰거나 바꾸거나 추가 가능
ADog, AMyEnemy
장점코드 재사용, 유지보수 쉬움부모 기능도 자식에서 쓸 수 있음

protected 외부 접근은 불가능하지만 상속 받은 클래스에서는 접근이 가능함

2️⃣ 멤버 초기화 리스트

#include <iostream>
#include <string>

using namespace std;

class Vehicle {
protected:
    string color;
    int speed;

public:
    Vehicle(string c, int s) : color(c), speed(s) {}  // 멤버 초기화 리스트

    void move() {
        cout << "The vehicle is moving at " << speed << " km/h." << endl;
    }

    void setColor(string c) {
        color = c;
    }

    string getColor() {
        return color;
    }
};


생성자 괄호 ( ) 뒤에 콜론 : 붙이고 멤버 변수나 부모 클래스 생성자를 초기화함
멤버 초기화 리스트는 생성자의 실제 코드 부분보다 먼저 실행됨

// 멤버 초기화 리스트 X
MyClass() {
   member = 10; // 생성된 후 값을 넣는 것 (대입), 생성 → 기본값 → 대입
}

// 멤버 초기화 리스트 O
MyClass() : member(10) {
   // 여기 오기 전에 이미 member가 10으로 초기화됨, 생성 → 초기화
}

다형성

  • 기본 클래스(Base Class) = 기본이 되는 클래스
    → 함수의 인터페이스만 정의 (실제 내용은 없음 또는 기본값만 있음)
    → 보통 virtual 로 가상 함수를 선언

  • 파생 클래스(Derived Class) = 실제 구현 담당
    override 로 함수를 재정의(오버라이딩) 해서 실제 동작을 다르게 구현함

1️⃣ 가상 함수 (Virtual Function)

  1. virtual 로 선언된 함수는 가상 함수
    class Animal {
    public:
       virtual void speak(); // 가상 함수
    };
  • virtual 키워드를 붙이면 이 함수는 동적 바인딩 대상이 됨
  • 실행 중(run-time)에 어떤 함수가 호출될지 결정됨

  1. 컴파일 시 가상 함수 테이블(V-Table) 생성됨
  • 클래스마다 하나의 가상 함수 테이블이 생김
  • 테이블 = 이 클래스가 가질 가상 함수들의 주소 들어 있음
  • 객체에는 V-Table을 가리키는 포인터(vptr)가 자동으로 포함됨

  1. 파생 클래스가 함수 재정의하면 V-Table에 새 함수 주소로 대체됨
    class Dog : public Animal {
    public:
       void speak() override; // 오버라이드 → Dog의 speak로 바뀜
    };
  • Animal의 speak()는 Dog에서 재정의
    → Dog의 vtable에는 Dog::speak() 의 주소가 들어감

  1. 부모 클래스 포인터로 호출해도 실제 객체의 함수가 호출됨
    int main() 
    {
    	Animal* a = new Dog();
    	a->speak(); // Dog::speak()가 호출됨
    }
  • aAnimal* 이지만 내부에 있는 vptr이 Dog의 vtable을 가리킴
    → 실제 Dog::speak() 주소를 찾아감

virtual 함수 → V-Table 생성 → override로 재정의 → 실행 시 실제 타입의 함수 호출

부분의미
Animal* a부모 클래스 포인터
new Dog()자식 클래스 객체를 생성
a->speak()부모 타입 포인터로 speak() 호출
Dog::speak()오버라이드 된 자식 함수

2️⃣ 순수 가상 함수 (Pure Virtual Function)

함수 선언만 있는 함수
인터페이스는 필요하지만 기본 클래스에서 구현할 필요가 없을 때 → 함수 틀만 제공

// 문법
virtual void 함수이름() = 0;

// 예시
class Animal {	// 추상 클래스
public:
    virtual void speak() = 0;  // 순수 가상 함수
};

구현은 없고 = 0 만 있음
이 클래스는 추상 클래스(Abstract Class)가 됨

✅ 추상 클래스

하나 이상의 순수 가상 함수를 가지는 클래스
→ 스스로는 객체를 만들 수 없고 기반 클래스로만 사용됨

  • 인스턴스화 불가 → new로 객체 생성 ❌
  • 포인터나 참조로는 사용 가능
  • 파생 클래스는 반드시 순수 가상 함수를 구현(override) 해야 함
    → 안 하면 파생 클래스도 추상 클래스가 됨
    • vs. 가상 함수
      • 자식 클래스에서 재정의는 선택
      • 안 해도 컴파일 에러 없음
구분가상 함수순수 가상 함수
선언virtual void func();virtual void func() = 0;
구현 여부부모 클래스에 구현 있음구현 없음 (틀만 존재)
오버라이드자식 클래스에서 선택자식 클래스에서 필수
객체 생성가능❌ 객체 생성 불가
역할선택적 재정의강제 인터페이스 제공

✅ 객체 vs 객체 포인터 — 멤버 접근 방법

상황예시연산자설명
객체Animal a;.객체 자체로 멤버에 접근
객체 포인터Animal* a = new Animal();->포인터로 객체를 가리킬 때 사용
int main() {
    Animal a;         // 객체
    Animal* aptr = &a; // 객체 포인터

    a.speak();        // 객체 → . 연산자 사용
    aptr->speak();    // 포인터 → -> 연산자 사용
}

0개의 댓글