C++ 추가 자료
포인터1.1 메모리와 변수메모리는 RAM의 일부로, 여러 칸으로 구성됨. 각 칸은 고유한 주소를 가짐.변수는 메모리 칸에 데이터를 저장하는 이름표 역할.변수 주소는 &를 통해 접근 가능.int a = 10; // a: 값 10 저장, &a: a의 주소1.2 포인터 기본 개념포인터는 다른 변수의 메모리 주소를 저장하는 변수.주요 연산:참조(&): 변수의 주소 얻기.역참조(*): 포인터가 가리키는 주소의 값 얻기.int* ptr = &a; // ptr은 a의 주소 저장1.3 메모리와 포인터 예제예시: 변수 a와 포인터 ptra가 0x100 주소에 저장되어 있고, 값은 10.ptr은 a의 주소(0x100)를 저장, *ptr은 10 반환.| 이름 | 주소 | 값 | 타입 || ---- | ----..
2024.12.27
챕터 1-3 : 객체지향 프로그래밍
학습 목표상속의 개념과 활용 이해기본 클래스와 파생 클래스 정의 및 구현접근 제어자(protected)의 역할과 사용법 학습다형성의 원리와 실제 활용 방안 숙지1. 상속1.1 상속 없는 차량 관리 프로그램기존 방식의 문제점중복 코드 증가유지보수 및 확장성 부족class Bicycle { string color; int speed; bool hasBasket;public: Bicycle(string c, int s, bool basket) : color(c), speed(s), hasBasket(basket) {} void move() { cout 문제점 요약공통 속성(color, speed)과 동작(move)이 각 클래스에 중복되어 구현됨.새로운 차량 클래스 추가 시 기존 코드..
2024.12.26
챕터 1-2 : Class 개념
학습 목표클래스의 역할 이해클래스와 객체를 정의하고 활용접근 제어자(public, private) 사용 1. 객체 없이 성적 관리 프로그램기존 방식은 데이터 노출 및 재사용성 부족 문제 존재double getAvg(int kor, int eng, int math) { return (kor + eng + math) / 3.0; }int getMax(int kor, int eng, int math) { return max({kor, eng, math}); }문제: 과목 추가 시 코드 수정 필요, 데이터가 외부에 노출됨. 2. 클래스 정의 및 멤버 구성클래스 구조멤버 변수: 데이터를 포함멤버 함수: 동작 정의class Student { int kor, eng, math; // 멤버 변수 ..
2024.12.24
no image
챕터 1-1 : 프로그래밍 기초
챕터 1-1 : 프로그래밍 기초1. 목표변수, 배열, 함수 등 프로그래밍 기본 요소를 이해하고 활용할 수 있다.문법 구조와 데이터 처리 방식을 익힌다.입력과 출력을 포함한 간단한 프로그램을 설계하고 구현할 수 있다.2. Visual Studio 설치Visual Studio Community는 무료로 제공되는 IDE로, C++ 관련 개발 환경을 구축하기 위해 설치."C++을 사용한 데스크톱 개발" 패키지를 설치하여 준비 완료.3. 첫 번째 프로그램: Hello, World!#include using namespace std;int main() { cout 4. 변수란?데이터를 저장할 공간.변수의 타입:int: 정수float, double: 소수점이 있는 실수char: 한 글자 문자bool: 논리값 (..
2024.12.23

C++ 추가 자료

언리얼 거토
|2024. 12. 27. 20:15
  1. 포인터

1.1 메모리와 변수

  • 메모리는 RAM의 일부로, 여러 칸으로 구성됨. 각 칸은 고유한 주소를 가짐.
  • 변수는 메모리 칸에 데이터를 저장하는 이름표 역할.
  • 변수 주소는 &를 통해 접근 가능.
  • int a = 10; // a: 값 10 저장, &a: a의 주소

1.2 포인터 기본 개념

  • 포인터는 다른 변수의 메모리 주소를 저장하는 변수.
  • 주요 연산:
    • 참조(&): 변수의 주소 얻기.
    • 역참조(*): 포인터가 가리키는 주소의 값 얻기.
    int* ptr = &a;  // ptr은 a의 주소 저장

1.3 메모리와 포인터 예제

  • 예시: 변수 a와 포인터 ptr
    • a가 0x100 주소에 저장되어 있고, 값은 10.
    • ptr은 a의 주소(0x100)를 저장, *ptr은 10 반환.
    | 이름 | 주소   | 값     | 타입  |
    | ---- | ------ | ------ | ----- |
    | a    | 0x100  | 10     | int   |
    | ptr  | 0x200  | 0x100  | int*  |

1.4 코드 예제

#include <iostream>
using namespace std;

int main() {
    int a = 10;       // 변수 a 선언
    int* ptr = &a;    // ptr은 a의 주소 저장

    cout << "a의 값: " << a << endl;         // 10
    cout << "a의 주소: " << &a << endl;     // 0x100 (a의 주소)
    cout << "ptr의 값: " << ptr << endl;    // 0x100 (ptr이 가리키는 주소)
    cout << "ptr이 가리키는 값: " << *ptr << endl; // 10 (ptr이 가리키는 값)
}

1.5 주의 사항

  • int* ptr에서 *는 포인터 타입 선언 의미.
  • *ptr에서 *는 역참조 연산 의미.

 

2. 오버로딩(Overloading)과 오버라이딩(Overriding)

2.1 오버로딩(Overloading)

개념

  • 같은 이름의 함수나 연산자를 여러 번 정의하는 것을 의미.
  • 매개변수 목록(개수 또는 타입)이 달라야 함.
  • 함수 호출 시 전달된 인자에 따라 적절한 함수가 호출됨.

특징

  • 같은 클래스 내에서 사용.
  • 반환값만 다르다고 해서 오버로딩이 성립되지 않음.
  • 컴파일 타임에 결정(정적 바인딩).

예시

<함수 오버로딩>

#include <iostream>
using namespace std;

class Calculator {
public:
    int add(int a, int b) { return a + b; }
    double add(double a, double b) { return a + b; }
    int add(int a, int b, int c) { return a + b + c; }
};

int main() {
    Calculator calc;

    cout << "정수 덧셈: " << calc.add(3, 4) << endl;        // 7
    cout << "실수 덧셈: " << calc.add(2.5, 3.5) << endl;  // 6.0
    cout << "세 정수 덧셈: " << calc.add(1, 2, 3) << endl; // 6

    return 0;
}

<연산자 오버로딩>

#include <iostream>
using namespace std;

class Complex {
private:
    double real, imag;

public:
    Complex(double r, double i) : real(r), imag(i) {}

    Complex operator+(const Complex& c) {
        return Complex(real + c.real, imag + c.imag);
    }

    void display() {
        cout << real << " + " << imag << "i" << endl;
    }
};

int main() {
    Complex c1(1.0, 2.0), c2(3.0, 4.0);
    Complex c3 = c1 + c2;

    c3.display(); // 4.0 + 6.0i

    return 0;
}

2.2 오버라이딩(Overriding)

개념

  • 기반 클래스의 메서드를 파생 클래스에서 재정의.
  • 부모 클래스의 메서드와 동일한 이름, 반환 타입, 매개변수 목록을 가짐.
  • 다형성을 구현하기 위해 사용.

특징

  • 기반 클래스와 파생 클래스 간 사용.
  • 반드시 상속 관계가 있어야 함.
  • 런타임에 결정(동적 바인딩).
  • 기반 클래스의 메서드는 virtual 키워드로 선언해야 함.

예시

#include <iostream>
using namespace std;

class Animal {
public:
    virtual void speak() {
        cout << "Animal speaks" << endl;
    }
};

class Dog : public Animal {
public:
    void speak() override {
        cout << "Dog barks" << endl;
    }
};

class Cat : public Animal {
public:
    void speak() override {
        cout << "Cat meows" << endl;
    }
};

int main() {
    Animal* animal;
    Dog dog;
    Cat cat;

    animal = &dog;
    animal->speak(); // Dog barks

    animal = &cat;
    animal->speak(); // Cat meows

    return 0;
}

2.3 오버로딩 vs 오버라이딩 차이점

구분오버로딩(Overloading)오버라이딩(Overriding)

정의 같은 클래스 내에서 함수 이름이 같지만 매개변수가 다르게 정의 파생 클래스에서 부모 클래스의 메서드를 재정의
적용 대상 같은 클래스 다른 클래스(상속 관계 필요)
매개변수 목록 반드시 달라야 함 동일해야 함
반환 타입 무관 동일해야 함
키워드 없음 virtual, override 사용 가능
바인딩 시점 컴파일 타임(정적 바인딩) 런타임(동적 바인딩)

2.4 객체 지향 프로그래밍과의 관계

오버로딩

  • 캡슐화와 관련.
  • 같은 이름의 메서드를 다양한 매개변수로 제공하여 사용자에게 단순한 인터페이스 제공.

오버라이딩

  • 상속과 다형성의 원칙 활용.
  • 부모 클래스의 메서드를 자식 클래스에서 재정의하여 객체의 동작을 맞춤화.

예시: 다형성과 오버라이딩

#include <iostream>
using namespace std;

class Vehicle {
public:
    virtual void start() {
        cout << "Vehicle is starting" << endl;
    }
};

class Car : public Vehicle {
public:
    void start() override {
        cout << "Car is starting" << endl;
    }
};

class Bike : public Vehicle {
public:
    void start() override {
        cout << "Bike is starting" << endl;
    }
};

void startVehicle(Vehicle* v) {
    v->start();
}

int main() {
    Car car;
    Bike bike;

    startVehicle(&car); // Car is starting
    startVehicle(&bike); // Bike is starting

    return 0;
}

'TIL > C++' 카테고리의 다른 글

챕터 2-2 : 템플릿  (1) 2024.12.31
챕터 2-1 : 자원 관리하기  (0) 2024.12.30
챕터 1-3 : 객체지향 프로그래밍  (1) 2024.12.26
챕터 1-2 : Class 개념  (0) 2024.12.24
챕터 1-1 : 프로그래밍 기초  (1) 2024.12.23

챕터 1-3 : 객체지향 프로그래밍

언리얼 거토
|2024. 12. 26. 15:59

학습 목표

  • 상속의 개념과 활용 이해
  • 기본 클래스와 파생 클래스 정의 및 구현
  • 접근 제어자(protected)의 역할과 사용법 학습
  • 다형성의 원리와 실제 활용 방안 숙지

1. 상속

1.1 상속 없는 차량 관리 프로그램

기존 방식의 문제점

  • 중복 코드 증가
  • 유지보수 및 확장성 부족
class Bicycle {
    string color;
    int speed;
    bool hasBasket;

public:
    Bicycle(string c, int s, bool basket) : color(c), speed(s), hasBasket(basket) {}
    void move() { cout << "The bicycle is moving at " << speed << " km/h." << endl; }
    void ringBell() { cout << "Bicycle bell: Ring Ring!" << endl; }
};

class Truck {
    string color;
    int speed;
    int cargoCapacity;

public:
    Truck(string c, int s, int capacity) : color(c), speed(s), cargoCapacity(capacity) {}
    void move() { cout << "The truck is moving at " << speed << " km/h." << endl; }
    void loadCargo() { cout << "Truck loading cargo. Capacity: " << cargoCapacity << " tons." << endl; }
};

문제점 요약

  • 공통 속성(color, speed)과 동작(move)이 각 클래스에 중복되어 구현됨.
  • 새로운 차량 클래스 추가 시 기존 코드 수정 필요.

1.2 상속을 활용한 클래스 정의

클래스 구조

  • 기본 클래스(Vehicle): 공통 속성과 동작 정의
  • 파생 클래스(Bicycle, Truck): 고유 특성 추가
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; }
};

Bicycle 클래스

class Bicycle : public Vehicle {
private:
    bool hasBasket;

public:
    Bicycle(string c, int s, bool basket) : Vehicle(c, s), hasBasket(basket) {}
    void ringBell() { cout << "Bicycle bell: Ring Ring!" << endl; }
};

Truck 클래스

class Truck : public Vehicle {
private:
    int cargoCapacity;

public:
    Truck(string c, int s, int capacity) : Vehicle(c, s), cargoCapacity(capacity) {}
    void loadCargo() { cout << "Truck loading cargo. Capacity: " << cargoCapacity << " tons." << endl; }
};

1.3 접근 제어자(protected)의 사용

  • protected: 파생 클래스에서 접근 가능하지만 외부에서는 접근 불가.
  • 효과: 공통 속성을 외부에 노출하지 않으면서 파생 클래스에서 재사용 가능.
class Vehicle {
protected:
    string color;
    int speed;
};

전체 프로그램 구현

int main() {
    Bicycle b("Yellow", 30, true);
    Truck t("Blue", 40, 95);

    b.move();
    b.ringBell();
    t.move();
    t.loadCargo();

    return 0;
}

결과: 공통 동작과 속성을 상속으로 관리하여 코드 중복을 제거하고 확장성을 확보.


2. 다형성 (Polymorphism)

2.1 다형성 정의

  • 다형성: 대표 클래스(부모 클래스)를 정의하고, 세부 구현은 이를 상속받는 파생 클래스에서 담당하도록 하는 기법.
  • 다양한 데이터 타입을 하나의 인터페이스로 처리할 수 있으며, 유지보수성과 확장성을 극대화함.

2.2 다형성 적용 전 코드

문제점

  • 동물마다 별도의 클래스와 함수를 생성해야 함.
  • 새로운 동물 클래스 추가 시, 기존 코드에 변경 사항이 생김.
  • 코드 중복이 발생하고, 유지보수가 어려움.
class Lion {
public:
    Lion(string word) : m_word(word) {}
    void bark() { cout << "Lion: " << m_word << endl; }
private:
    string m_word;
};

class Wolf {
public:
    Wolf(string word) : m_word(word) {}
    void bark() { cout << "Wolf: " << m_word << endl; }
private:
    string m_word;
};

class Dog {
public:
    Dog(string word) : m_word(word) {}
    void bark() { cout << "Dog: " << m_word << endl; }
private:
    string m_word;
};

void print(Lion lion) {
    lion.bark();
}

void print(Wolf wolf) {
    wolf.bark();
}

void print(Dog dog) {
    dog.bark();
}

int main() {
    Lion lion("ahaaaaaa!");
    Wolf wolf("ohhhhh");
    Dog dog("oooooooooooooops");

    print(lion);
    print(wolf);
    print(dog);

    return 0;
}

2.3 다형성 적용

개선 방법

  • 공통 부모 클래스 생성: 모든 동물 클래스가 상속받는 Animal 클래스 정의.
  • 가상 함수 사용: 부모 클래스에 virtual 키워드를 사용해 공통 메서드(bark) 정의.
  • 동적 바인딩: 부모 클래스 포인터를 사용하여 적절한 자식 클래스의 메서드 호출.
class Animal {
public:
    virtual void bark() = 0; // 순수 가상 함수
};

class Lion : public Animal {
private:
    string m_word;

public:
    Lion(string word) : m_word(word) {}
    void bark() override { cout << "Lion: " << m_word << endl; }
};

class Wolf : public Animal {
private:
    string m_word;

public:
    Wolf(string word) : m_word(word) {}
    void bark() override { cout << "Wolf: " << m_word << endl; }
};

class Dog : public Animal {
private:
    string m_word;

public:
    Dog(string word) : m_word(word) {}
    void bark() override { cout << "Dog: " << m_word << endl; }
};


void print(Animal* animal) { //부모 클래스와 상속 객체를 가리킬 수 있다//
    animal->bark(); //동적 바인딩//
}

int main() {
    Lion lion("ahaaaaaa!");
    Wolf wolf("ohhhhh");
    Dog dog("oooooooooooooops");

    print(&lion);
    print(&wolf);
    print(&dog);

    return 0;
}

3. 포인터 개념과 활용

3.1 포인터란?

  • 포인터(pointer)는 변수의 메모리 주소를 저장하는 특별한 변수.
  • C++에서 포인터는 강력하면서도 위험한 도구로, 잘못된 사용 시 프로그램 충돌이나 메모리 누수 등을 초래할 수 있음.

포인터 기본 문법

int main() {
    int a = 10;
    int* p = &a; // 'p'는 'a'의 메모리 주소를 저장

    cout << "a의 값: " << a << endl; // 10 출력
    cout << "a의 주소: " << &a << endl; // 메모리 주소 출력
    cout << "p가 가리키는 값: " << *p << endl; // 'p'가 가리키는 값, 즉 10 출력

    *p = 20; // 'p'를 통해 'a'의 값을 수정
    cout << "a의 새로운 값: " << a << endl; // 20 출력

    return 0;
}

3.2 포인터와 배열

  • 배열은 포인터와 밀접한 연관이 있음.
  • 배열 이름은 첫 번째 요소의 주소를 가리키는 포인터로 사용 가능.
int main() {
    int arr[3] = {1, 2, 3};
    int* p = arr; // 배열 이름은 첫 번째 요소의 주소를 나타냄

    cout << *p << endl; // 1 출력
    cout << *(p + 1) << endl; // 2 출력
    cout << *(p + 2) << endl; // 3 출력

    return 0;
}

3.3 포인터의 위험성

  • 초기화되지 않은 포인터 사용
  • 메모리 해제 후 포인터 사용
  • 잘못된 주소에 접근

스마트 포인터

  • C++11에서 도입된 스마트 포인터는 메모리 누수를 방지하는 데 유용.
  • 주요 타입: std::unique_ptr, std::shared_ptr, std::weak_ptr.
#include <memory>
#include <iostream>
using namespace std;

int main() {
    unique_ptr<int> uptr = make_unique<int>(42); // 동적 메모리 자동 관리
    cout << *uptr << endl; // 42 출력

    return 0;
}

 

4. 다형성을 활용한 게임 스킬 사용 프로그램(숙제)

4.1 숙제 개요

다형성을 활용하여 다양한 모험가들이 고유의 스킬을 사용하는 프로그램을 구현합니다. 이는 객체지향 프로그래밍(OOP)의 핵심 개념인 상속과 다형성을 실제로 적용하는 예제입니다.


4.2 프로그램 구현

기본 클래스

Adventure라는 기본 클래스를 정의하며, 모든 모험가 클래스의 부모로 사용

class Adventure {
public:
    virtual void useSkill() = 0; // 순수 가상 함수
    virtual ~Adventure() {} // 가상 소멸자
};

파생 클래스

각 직업(전사, 마법사, 궁수)의 스킬을 재정의

class Warrior : public Adventure {
public:
    void useSkill() override { 
        cout << "Warrior uses Slash!" << endl; 
    }
};

class Mage : public Adventure {
public:
    void useSkill() override { 
        cout << "Mage casts Fireball!" << endl; 
    }
};

class Archer : public Adventure {
public:
    void useSkill() override { 
        cout << "Archer shoots an Arrow!" << endl; 
    }
};

다형성 구현

Adventure 포인터를 사용하여 다양한 모험가 객체를 가리키고, 반복문으로 각 객체의 스킬을 호출

int main() {
    // 모험가 객체 생성
    Warrior warrior;
    Mage mage;
    Archer archer;

    // 모험가 리스트 생성
    Adventure* adventurers[] = {&warrior, &mage, &archer};

    // 모든 모험가의 스킬 호출
    for (Adventure* adventurer : adventurers) {
        adventurer->useSkill();
    }

    return 0;
}

4.3 실행 결과

프로그램 실행 시, 각 모험가의 스킬이 호출됨

Warrior uses Slash!
Mage casts Fireball!
Archer shoots an Arrow!

4.4 학습 포인트

  1. 다형성의 이점: 공통된 인터페이스(Adventure)를 통해 코드를 간결하고 확장 가능하게 유지.
  2. 가상 함수 활용: 각 파생 클래스의 고유 동작을 동적으로 호출.
  3. 순수 가상 함수: 추상 클래스를 통해 강력한 구조적 제약 제공.

최종 요약

위의 예제는 다형성을 활용한 객체지향 프로그래밍의 핵심 원리를 잘 보여줍니다. 다양한 객체를 동일한 방식으로 처리할 수 있으며, 코드의 재사용성과 유지보수성을 높입니다. 이를 통해 실무적인 응용 프로그램에서도 구조적 효율성을 높이는 방법을 배웠습니다.

 

 

 

과제 임시 저장

필수 과제:

#include <iostream>
using namespace std;

class Animal {
public:
    virtual void makeSound() = 0;  // 순수 가상 함수
    virtual ~Animal() {}  // 가상 소멸자
};

class Dog : public Animal {
public:
    void makeSound() override { cout << "Bow Wow" << endl; } //overrisde로 makeSound 재정의
};

class Cat : public Animal {
public:
    void makeSound() override { cout << "Meow Meow" << endl; }
};

class Cow : public Animal {
public:
    void makeSound() override { cout << "Moo Moo" << endl; }
};

int main() {
    Dog dog;
    Cat cat;
    Cow cow;
    
    //다형성
    Animal* animals[] = { &dog, &cat, &cow };
    for (Animal* animal : animals) {  // animals 배열 안에 있는 Animal 타입의 animal의 주소들
        animal->makeSound();
    }
}

 

도전 과제:

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

// Animal 기본 클래스
class Animal {
public:
    virtual void makeSound() = 0;  // 순수 가상 함수
    virtual ~Animal() {}  // 가상 소멸자
};

// Dog 클래스
class Dog : public Animal {
public:
    void makeSound() override { cout << "Bow Wow" << endl; }
};

// Cat 클래스
class Cat : public Animal {
public:
    void makeSound() override { cout << "Meow Meow" << endl; }
};

// Cow 클래스
class Cow : public Animal {
public:
    void makeSound() override { cout << "Moo Moo" << endl; }
};

// Zoo 클래스
class Zoo {
private:
    Animal* animals[10];  // 동물 객체를 저장하는 동적 배열
    int animalCount = 0;   // 현재 동물 개수

public:
    // 동물을 동물원에 추가하는 함수
    void addAnimal(Animal* animal) {
        if (animalCount < 10) {
            animals[animalCount++] = animal;  // 동물 객체를 배열에 추가
        } else {
            cout << "동물원에 더 이상 동물을 추가할 수 없습니다!" << endl;
        }
    }

    // 동물원에 있는 모든 동물의 행동을 수행하는 함수
    void performActions() {
        for (int i = 0; i < animalCount; ++i) {  // 모든 동물 객체에 대해 순차적으로 소리 내기
            animals[i]->makeSound();
        }
    }

    // Zoo 소멸자
    ~Zoo() {
        for (int i = 0; i < animalCount; ++i) {
            delete animals[i];  // 동적 할당된 동물 객체 메모리 해제
        }
    }
};

// 랜덤 동물을 생성하는 함수
Animal* createRandomAnimal() {
    int random = rand() % 3;  // 랜덤 수를 3을 나눈 나머지. 0, 1, 2 중 하나가 생성됨

    // 난수 값에 따라 Dog, Cat, Cow 객체를 동적으로 생성하고 그 주소값(포인터)을 반환
    if (random == 0) {
        return new Dog();
    } else if (random == 1) {
        return new Cat();
    } else {
        return new Cow();
    }
}

int main() {
    srand(static_cast<unsigned int>(time(0))); // 난수 생성, 랜덤 수를 양수로 형 변형

    Zoo zoo;

    // 동물 5마리를 랜덤으로 생성하여 동물원에 추가
    for (int i = 0; i < 5; ++i) {
        Animal* randomAnimal = createRandomAnimal();
        zoo.addAnimal(randomAnimal);  // 동물원에 동물 추가
    }

    // 동물들의 소리와 행동을 실행
    zoo.performActions();

    return 0;
}
더보기

Zoo 클래스

class Zoo {
private:
    Animal* animals[10];  // 동물 객체를 저장하는 동적 배열
    int animalCount = 0;   // 현재 동물 개수

public:
    void addAnimal(Animal* animal) {
        if (animalCount < 10) {
            animals[animalCount++] = animal;  // 동물 객체를 배열에 추가
        } else {
            cout << "동물원에 더 이상 동물을 추가할 수 없습니다!" << endl;
        }
    }

    void performActions() {
        for (int i = 0; i < animalCount; ++i) {
            animals[i]->makeSound();  // 모든 동물 객체에 대해 순차적으로 소리 내기
        }
    }

    ~Zoo() {
        for (int i = 0; i < animalCount; ++i) {
            delete animals[i];  // 동적 할당된 동물 객체 메모리 해제
        }
    }
};
 
  • Zoo 클래스는 동물들을 관리하는 클래스입니다.
  • animals[10]: 최대 10마리 동물을 저장할 수 있는 Animal* 타입의 고정 크기 배열입니다.
  • animalCount: 현재 동물원에 저장된 동물의 개수를 추적하는 변수입니다. 이를 통해 동물의 추가와 삭제를 관리합니다.

addAnimal 함수

void addAnimal(Animal* animal) {
    if (animalCount < 10) {
        animals[animalCount++] = animal;  // 동물 객체를 배열에 추가
    } else {
        cout << "동물원에 더 이상 동물을 추가할 수 없습니다!" << endl;
    }
}
  • addAnimal 함수는 동물 객체를 받아 animals 배열에 추가하는 함수입니다.
  • animalCount가 10 미만일 때만 동물을 추가할 수 있도록 조건문을 사용하여 배열 크기를 초과하는 동물이 추가되지 않도록 방지합니다.
  • 동물 객체가 추가될 때마다 animalCount를 증가시켜, 추가된 동물의 개수를 추적합니다.

~Zoo 소멸자

~Zoo() {
    for (int i = 0; i < animalCount; ++i) {
        delete animals[i];  // 동적 할당된 동물 객체 메모리 해제
    }
}
  • ~Zoo() 소멸자는 Zoo 객체가 소멸될 때 호출됩니다. 이때 동물원에 추가된 동물 객체들이 동적으로 생성되었기 때문에 delete를 통해 메모리를 해제합니다.
  • animalCount만큼 반복하여, animals 배열에 저장된 각 동물 객체의 메모리를 해제합니다.

이 코드는 동물 객체를 생성하고, 동물원에 추가하며, 동물들의 소리와 행동을 출력하는 프로그램입니다. 각 동물 클래스는 Animal 클래스를 상속받아 다형성을 구현하며, Zoo 클래스는 동물 객체들을 관리하고 소멸 시 동적 메모리 해제를 통해 메모리 누수를 방지합니다.

'TIL > C++' 카테고리의 다른 글

챕터 2-2 : 템플릿  (1) 2024.12.31
챕터 2-1 : 자원 관리하기  (0) 2024.12.30
C++ 추가 자료  (1) 2024.12.27
챕터 1-2 : Class 개념  (0) 2024.12.24
챕터 1-1 : 프로그래밍 기초  (1) 2024.12.23

챕터 1-2 : Class 개념

언리얼 거토
|2024. 12. 24. 20:00

학습 목표

  • 클래스의 역할 이해
  • 클래스와 객체를 정의하고 활용
  • 접근 제어자(public, private) 사용

 

1. 객체 없이 성적 관리 프로그램

  • 기존 방식은 데이터 노출 및 재사용성 부족 문제 존재
double getAvg(int kor, int eng, int math) { return (kor + eng + math) / 3.0; }
int getMax(int kor, int eng, int math) { return max({kor, eng, math}); }
  • 문제: 과목 추가 시 코드 수정 필요, 데이터가 외부에 노출됨.

 

2. 클래스 정의 및 멤버 구성

  • 클래스 구조
    • 멤버 변수: 데이터를 포함
    • 멤버 함수: 동작 정의
class Student {
    int kor, eng, math;       // 멤버 변수
    double getAvg();          // 멤버 함수
    int getMaxScore();
};

.

3. 클래스 구현

  • 클래스 내부 구현
class Student {
    int kor, eng, math;
    double getAvg() { return (kor + eng + math) / 3.0; }
    int getMaxScore() { return max({kor, eng, math}); }
};

 

  • 클래스 외부 구현
double Student::getAvg() { return (kor + eng + math) / 3.0; }
int Student::getMaxScore() { return max({kor, eng, math}); }

 

4. 접근 제어자

  • public: 외부 접근 가능
  • private: 외부 접근 불가
class Student {
public:
    double getAvg();
    int getMaxScore();
private:
    int kor, eng, math;
};

 

5. Getter와 Setter

  • 사용 이유: private 멤버 접근 및 수정
class Student {
public:
    void setKor(int k) { kor = k; }
    int getKor() { return kor; }
private:
    int kor, eng, math;
};

 

 

6. 생성자(Constructor)

생성자는 객체가 생성될 때 호출되는 특수한 멤버 함수로, 클래스와 동일한 이름을 가지며 반환 타입이 없다. 생성자를 활용해 객체를 다양한 방식으로 초기화할 수 있으며, 이를 **생성자 오버로딩(Constructor Overloading)**이라고 한다.

#include <iostream>
#include <string>
using namespace std;

class Car {
    string brand;
    int year;
public:
    Car() : brand("Unknown"), year(0) {} // 기본 생성자
    Car(string b, int y) : brand(b), year(y) {} // 매개변수가 있는 생성자

    void display() {
        cout << "Brand: " << brand << ", Year: " << year << endl;
    }
};

int main() {
    Car car1; // 기본 생성자 호출
    Car car2("Hyundai", 2023); // 매개변수가 있는 생성자 호출

    car1.display();
    car2.display();

    return 0;
}

기본 생성자는 초기값을 설정하며, 매개변수가 있는 생성자는 입력받은 값을 기반으로 멤버 변수를 초기화한다.

 

7. 헤더와 소스 파일 분리

C++에서는 코드 가독성과 유지보수를 위해 클래스 선언과 구현을 분리하는 것이 일반적이다. 헤더 파일(.h)에는 클래스 선언을, 소스 파일(.cpp)에는 해당 클래스의 구현을 작성한다.

예제: Car 클래스 헤더와 소스 파일 분리

  • Car.h (헤더 파일)
#ifndef CAR_H
#define CAR_H

#include <string>
using namespace std;

class Car {
    string brand;
    int year;
public:
    Car();
    Car(string b, int y);
    void display();
};

#endif

 

  • Car.cpp (소스 파일)
#include "Car.h"
#include <iostream>
using namespace std;

Car::Car() : brand("Unknown"), year(0) {}

Car::Car(string b, int y) : brand(b), year(y) {}

void Car::display() {
    cout << "Brand: " << brand << ", Year: " << year << endl;
}

 

  • main.cpp (메인 파일)
#include "Car.h"

int main() {
    Car car1;
    Car car2("Hyundai", 2023);

    car1.display();
    car2.display();

    return 0;
}

위 구조에서는 클래스를 사용하는 코드를 main.cpp에 작성하며, Car.h와 Car.cpp가 각각 선언과 구현을 담당한다.

 

4. 결론

  • 오늘 학습한 주요 내용:
    1. 생성자를 활용한 객체 초기화.
    2. 클래스 설계를 파일로 나누어 재사용성 및 유지보수성을 강화.
    3. 헤더 및 소스 파일 구조를 통한 효율적인 코드 관리.
  • 생성자는 객체의 초기화를 간단하게 하고, 파일 분리 기법은 대규모 프로젝트에서 필수적이다.

'TIL > C++' 카테고리의 다른 글

챕터 2-2 : 템플릿  (1) 2024.12.31
챕터 2-1 : 자원 관리하기  (0) 2024.12.30
C++ 추가 자료  (1) 2024.12.27
챕터 1-3 : 객체지향 프로그래밍  (1) 2024.12.26
챕터 1-1 : 프로그래밍 기초  (1) 2024.12.23

챕터 1-1 : 프로그래밍 기초

언리얼 거토
|2024. 12. 23. 13:08

챕터 1-1 : 프로그래밍 기초


1. 목표

  • 변수, 배열, 함수 등 프로그래밍 기본 요소를 이해하고 활용할 수 있다.
  • 문법 구조와 데이터 처리 방식을 익힌다.
  • 입력과 출력을 포함한 간단한 프로그램을 설계하고 구현할 수 있다.

2. Visual Studio 설치

  • Visual Studio Community는 무료로 제공되는 IDE로, C++ 관련 개발 환경을 구축하기 위해 설치.
  • "C++을 사용한 데스크톱 개발" 패키지를 설치하여 준비 완료.

3. 첫 번째 프로그램: Hello, World!

#include <iostream>
using namespace std;

int main() {
    cout << "Hello, World!" << endl;  // 화면에 텍스트 출력
    return 0;  // 프로그램 정상 종료
}

4. 변수란?

  • 데이터를 저장할 공간.
  • 변수의 타입:
    • int: 정수
    • float, double: 소수점이 있는 실수
    • char: 한 글자 문자
    • bool: 논리값 (true/false)
    • string: 문자열
    • unsigned: 양수만 저장

코드 예제: 계산기 프로그램

#include <iostream>
using namespace std;

int main() {
    int num1, num2;
    cout << "첫 번째 숫자를 입력하세요: ";
    cin >> num1;
    cout << "두 번째 숫자를 입력하세요: ";
    cin >> num2;

    int sum = num1 + num2;
    cout << "두 숫자의 합: " << sum << endl;

    return 0;
}

5. 배열이란?

  • 여러 데이터를 하나의 변수로 관리할 수 있는 구조.
  • 예제: 학생 5명의 점수를 입력받아 총점과 평균 계산.
#include <iostream>
using namespace std;

int main() {
    int scores[5];
    int total = 0;

    cout << "학생 점수를 입력하세요 (5명): " << endl;
    for (int i = 0; i < 5; i++) {
        cout << i + 1 << "번째 학생 점수: ";
        cin >> scores[i];
        total += scores[i];
    }

    double average = total / 5.0;
    cout << "총점: " << total << ", 평균: " << average << endl;

    return 0;
}

6. 함수란?

  • 반복되는 작업을 효율적으로 처리하기 위해 코드 블록을 정의하고 재사용.
  • 원의 넓이 계산 함수.
#include <iostream>
#include <cmath>  // M_PI 상수 사용
using namespace std;

double calculateArea(double radius) {
    return M_PI * radius * radius;
}

int main() {
    double radius;
    cout << "원의 반지름을 입력하세요: ";
    cin >> radius;

    double area = calculateArea(radius);
    cout << "원의 넓이: " << area << endl;

    return 0;
}

7. 조건문

  • 조건에 따라 프로그램의 흐름을 제어.

코드 예제: 숫자의 홀짝 판별

#include <iostream>
using namespace std;

int main() {
    int num;
    cout << "숫자를 입력하세요: ";
    cin >> num;

    if (num % 2 == 0) {
        cout << num << "은(는) 짝수입니다." << endl;
    } else {
        cout << num << "은(는) 홀수입니다." << endl;
    }

    return 0;
}
 

8. 반복문 (for & while)

  • for문과 while문을 사용하여 반복 작업 수행.

코드 예제: 1부터 10까지 합 계산 (for문 사용)

#include <iostream>
using namespace std;

int main() {
    int sum = 0;
    for (int i = 1; i <= 10; i++) {
        sum += i;
    }
    cout << "1부터 10까지의 합: " << sum << endl;

    return 0;
}

 


오늘의 요약

  • 프로그래밍 기초는 변수, 배열, 함수, 조건문 등으로 구성됨.
  • 간단한 프로그램부터 시작하여 점차적으로 복잡한 기능을 구현할 수 있음.
  • Visual Studio를 활용해 실제 코드를 작성하고 실행하면서 실습 능력을 향상시킬 수 있음.

느낀 점:
처음엔 조금 복잡해 보였지만, 한 단계씩 따라가다 보니 프로그래밍의 기본 개념을 이해할 수 있었다. 앞으로 더 많은 실습을 통해 익숙해질 필요성을 느꼈다. 🎯

'TIL > C++' 카테고리의 다른 글

챕터 2-2 : 템플릿  (1) 2024.12.31
챕터 2-1 : 자원 관리하기  (0) 2024.12.30
C++ 추가 자료  (1) 2024.12.27
챕터 1-3 : 객체지향 프로그래밍  (1) 2024.12.26
챕터 1-2 : Class 개념  (0) 2024.12.24