makeSound()라는 순수 가상 함수를 포함합니다.
class Zoo {
private:
Animal* animals[10]; // 동물 객체를 저장하는 포인터 배열
public:
// 동물을 동물원에 추가하는 함수
// - Animal 객체의 포인터를 받아 포인터 배열에 저장합니다.
// - 같은 동물이라도 여러 번 추가될 수 있습니다.
// - 입력 매개변수: Animal* (추가할 동물 객체)
// - 반환값: 없음
void addAnimal(Animal* animal);
// 동물원에 있는 모든 동물의 행동을 수행하는 함수
// - 모든 동물 객체에 대해 순차적으로 소리를 내고 움직이는 동작을 실행합니다.
// - 입력 매개변수: 없음
// - 반환값: 없음
void performActions();
// Zoo 소멸자
// - Zoo 객체가 소멸될 때, 동물 벡터에 저장된 모든 동물 객체의 메모리를 해제합니다.
// - 메모리 누수를 방지하기 위해 동적 할당된 Animal 객체를 `delete` 합니다.
// - 입력 매개변수: 없음
// - 반환값: 없음
~Zoo();
};
#include <cstdlib>
#include <ctime>
// 랜덤 동물을 생성하는 함수
// - 0, 1, 2 중 하나의 난수를 생성하여 각각 Dog, Cat, Cow 객체 중 하나를 동적으로 생성합니다.
// - 생성된 객체는 Animal 타입의 포인터로 반환됩니다.
// - 입력 매개변수: 없음
// - 반환값: Animal* (생성된 동물 객체의 포인터)
Animal* createRandomAnimal();
#pragma once
#include <iostream>
#include <cstdlib>
#include <ctime>
//Animal이라는 기본 클래스를 정의 합니다.
//Animal 클래스에는 `makeSound()`라는 순수 가상 함수를 포함합니다.
class Animal {
public:
virtual void makeSound() = 0;
virtual ~Animal() {}
};
//Animal 클래스를 상속받아 다양한 동물 클래스를 생성합니다. 예) Dog, Cat, Cow
class Dog : public Animal {
public:
//각 클래스에서 makeSound()함수를 재정의하여 해당 동물의 소리를 출력합니다.
void makeSound() override;
};
class Cat : public Animal {
public:
void makeSound() override;
};
class Cow : public Animal {
public:
void makeSound() override;
};
class Zoo {
private:
Animal* animals[10]; // 동물 객체를 저장하는 포인터 배열
int animalCount = 0; // 현재 등록된 동물 수
public:
// 동물을 동물원에 추가하는 함수
void addAnimal(Animal* animal);
// 동물원에 있는 모든 동물의 행동을 수행하는 함수
void performActions();
// Zoo 소멸자
~Zoo();
};
// 랜덤 동물을 생성하는 함수
Animal* createRandomAnimal();
#include <iostream>
#include "HW02.h"
using namespace std;
void Dog::makeSound() {
cout << "Dog : Bark" << endl;
}
void Cat::makeSound() {
cout << "Cat : Meow" << endl;
}
void Cow::makeSound() {
cout << "Cow : Moo" << endl;
}
// 동물을 동물원에 추가하는 함수
void Zoo::addAnimal(Animal* animal) {
if (animalCount < 10) {
animals[animalCount++] = animal;
}
else {
cout << "동물원이 가득 찼습니다." << endl;
}
}
// 동물원에 있는 모든 동물의 행동을 수행하는 함수
void Zoo::performActions() {
for (int i = 0; i < animalCount; i++) {
animals[i]->makeSound();
}
}
// Zoo 소멸자
Zoo ::~Zoo() {
for (int i = 0; i < animalCount; i++) {
delete animals[i];
}
}
// 랜덤 동물을 생성하는 함수
Animal* createRandomAnimal() {
int r = rand() % 3;
switch (r) {
case 0: return new Dog();
case 1: return new Cat();
case 2: return new Cow();
default: return nullptr;
}
}
int main()
{
cout << "- - - - - - 필수 기능 - - - - - -" << endl;
// 메인 함수에서 Animal 타입의 포인터 배열을 선언합니다.
Animal* animal[3];
// Dog, Cat, Cow 자료형의 변수를 선언하고, 배열에 저장해봅니다.
animal[0] = new Dog();
animal[1] = new Cat();
animal[2] = new Cow();
// Animal 배열을 반복문으로 순회하면서 각 동물의 울음소리를 내게 합니다.
for (int i = 0; i < 3; i++) {
animal[i]->makeSound();
}
for (int i = 0; i < 3; i++) {
delete animal[i];
}
cout << endl << "- - - - - - 도전 기능 - - - - - -" << endl;
srand(static_cast<unsigned int>(time(NULL)));
Zoo zoo;
for (int i = 0; i < 10; ++i) {
zoo.addAnimal(createRandomAnimal());
}
cout << "동물원에서 들리는 소리" << endl;
zoo.performActions();
return 0;
}
C/C++에서 난수(랜덤 값)를 생성할 때 rand() 함수를 사용 할 수 있다.
#include <cstdlib> // rand() 함수는 이 헤더에 정의되어 있음
#include <ctime> // 시간 관련 함수 time()
int main() {
srand(time(0)); // 랜덤 시드 설정 (한 번만)
int randomValue = rand(); // 0부터 RAND_MAX 사이의 정수 반환
cout << randomValue << endl;
}
| 항목 | 설명 |
|---|---|
| 반환값 | 0 이상 RAND_MAX 이하의 정수 (보통 32,767) |
| 헤더파일 | (#include <stdlib.h>도 가능) |
| 시드 설정 | srand(seed);로 설정. 같은 시드 → 같은 난수열 |
| 초기화 추천 | srand(time(0));로 현재 시간을 시드로 줘서 매번 다른 난수 생성 |
- 0 ~ 2 사이의 값 (3개 중 하나)
int r = rand() % 3; // 0, 1, 2 중 하나
- a ~ b 범위
int r = rand() % (b - a + 1) + a;
- 10~20 사이 값
int r = rand() % 11 + 10; // (20 - 10 + 1) + 10
C++에서 override 키워드는 자식 클래스에서 부모 클래스의 가상 함수를 재정의(오버라이딩) 할 때 사용한다.
반드시 써야 하는 건 아니지만, 매우 권장되는 좋은 습관이다.
▶ 실수 방지
부모 클래스 함수와 정확히 일치하지 않으면 컴파일 에러가 난다.
class Cat : public Animal {
public:
void makesound() const override { // ❌ 오타 → 컴파일 에러 발생!
std::cout << "Meow!\n";
}
};
makesound는 makeSound가 아니라서 오버라이딩이 아니라 새 함수가 된다.
override를 안 썼다면 다른 함수가 추가된 걸로 처리되어 버그 발생 가능.
class A {
public:
virtual void func(int x) const;
};
class B : public A {
public:
void func(int x) const override; // ✅ const까지 정확히 일치해야 함
};
| 키워드 | 의미 |
|---|---|
| virtual | 부모 클래스에서 오버라이딩 가능한 함수 표시 |
| override | 자식 클래스에서 정확히 오버라이딩한다고 표시 |
그렇다, 기본적으로 부모 클래스에서 함수가 virtual로 선언되어 있어야 자식 클래스에서 진짜 오버라이딩이 된다.
만약 부모 함수가 virtual이 아니면, 자식 클래스에서 같은 이름과 시그니처로 함수를 다시 만들 수는 있지만, 이건 함수 숨기기(hiding) 일 뿐이고, 가상 함수 호출(다형성)은 작동하지 않는다.
#include <iostream>
using namespace std;
class Parent {
public:
virtual void speak() { // virtual 함수
cout << "부모가 말해요" << endl;
}
void walk() { // virtual 아님
cout << "부모가 걷는다" << endl;
}
};
class Child : public Parent {
public:
void speak() override { // 오버라이딩
cout << "자식이 말해요" << endl;
}
void walk() { // 함수 숨기기
cout << "자식이 걷는다" << endl;
}
};
int main() {
Parent* p = new Child();
p->speak(); // 자식의 speak()가 호출됨 (가상 함수)
p->walk(); // 부모의 walk()가 호출됨 (비가상 함수)
delete p;
return 0;
}
speak()는 부모에서 virtual이라서 자식에서 재정의 가능하고, 포인터를 통해 호출하면 자식 버전이 호출된다
walk()는 부모에서 virtual이 아니기 때문에, 포인터를 통해 호출하면 항상 부모 버전이 호출된다.