[TIL] 2024-12-24
2024-12-24 TIL
버퍼 오버런, 버퍼 오버플로우라는 단어를 배웠습니다.
배열의 인덱스를 넘어선 접근을 버퍼 오버런이라고 합니다.
추상 클래스의 포인터 동적 배열은 생성할 수 없다는 것을 알게됐습니다.
std::string
의 인덱스에 접근을 하거나 push_back
할 경우 값을 char
자료형으로 넣어야 한다는 점을 알게됐습니다.
알고리즘 문제
문제가 간단해서 쉽게 풀고, 마무리 했습니다.
프로그래머스 x만큼 간격이 있는 n개의 숫자
기존 다른 알고리즘과 크게 다르지 않고 반복문을 사용하는 문제라 쉽게 풀었습니다.
프로그래머스 핸드폰 번호 가리기
스파르타 내일배움 부트캠프
사용자가 입력한 숫자 배열을 정렬하는 프로그램을 만드는 과제가 주어졌습니다.
조건은 다음과 같습니다.
정렬은 오름차순 정렬과 내림차순 정렬이 가능해야합니다.
숫자 1을 입력 받으면 오름차순 정렬, 숫자 2를 입력 받으면 내림차순 정렬을 하도록 구현해주세요.
algorithm
헤더의 sort
함수를 사용하지 않고 직접 구현해야합니다.
오름차순 정렬과 내림차순 정렬 동작을 각각 함수로 구현해야합니다.
main.cpp
#include <iostream>
#include "CustomVector.h"
// CH2 간단한 프로그래밍 구현 1번 도전 과제
// 입력을 받는 함수
void InputNumber(CustomVector& NumberArr, int& sortOrder)
{
int maxInput;
std::cout << "입력하려는 정수의 갯수를 입력해주세요" << std::endl;
std::cin >> maxInput;
for (int i = 0; i < maxInput; ++i)
{
int inputValue = 0;
std::cout << i << " 번째 정수의 값을 입력해주세요. : ";
std::cin >> inputValue;
NumberArr.push_back(inputValue);
}
std::cout << "정수의 입력을 마쳤습니다." << std::endl;
std::cout << "=================================" << std::endl;
std::cout << "오름차순 1 내림차순 2" << std::endl;
std::cout << "어떻게 정렬하시겠습니까?" << std::endl;
std::cin >> sortOrder;
}
// 병함 정렬을 위해 배열을 합치고 정렬하는 함수
void merge(CustomVector& array, int left, int mid, int right, int sortOrder)
{
// 임시 배열의 크기
int n1 = mid - left + 1;
int n2 = right - mid;
// 임시 배열 생성
CustomVector L(n1), R(n2);
for (int i = 0; i < n1; ++i)
{
L[i] = array[left + i];
}
for (int j = 0; j < n2; ++j)
{
R[j] = array[mid + j + 1];
}
// 임시 배열의 값으로 정렬 후 원본 배열의 값 수정
int i = 0, j = 0, k = left;
while (i < n1 && j < n2)
{
if (sortOrder == 1)
{
(L[i] <= R[j]) ? array[k++] = L[i++] : array[k++] = R[j++];
}
else
{
(L[i] >= R[j]) ? array[k++] = L[i++] : array[k++] = R[j++];
}
}
// 임시 배열의 남은 값 대입
while (i < n1)
{
array[k++] = L[i++];
}
while (j < n2)
{
array[k++] = R[j++];
}
}
// 병합 정렬을 위해 배열을 분리하는 함수
void mergeSort(CustomVector& array, int left, int right, int sortOrder)
{
if (left < right)
{
int mid = left + (right - left) / 2;
mergeSort(array, left, mid, sortOrder);
mergeSort(array, mid + 1, right, sortOrder);
merge(array, left, mid, right, sortOrder);
}
}
// 출력하는 함수
void PrintResult(CustomVector& NumberArr)
{
for (int i = 0; i < NumberArr.size(); ++i)
{
std::cout << NumberArr[i];
if (i + 1 != NumberArr.size())
{
std::cout << ", ";
}
}
}
int main()
{
CustomVector NumberArr{};
int sortOrder;
InputNumber(NumberArr, sortOrder);
mergeSort(NumberArr, 0, NumberArr.size() - 1, sortOrder);
PrintResult(NumberArr);
}
배열은 직접 벡터와 비슷하게 23일에 구현해본 동적 배열을 사용했습니다.
정렬 알고리즘은 병합 알고리즘을 사용했습니다.
구현코드는 아래 링크의 깃허브에서 직접 볼 수 있습니다.
Sparta_CH_2
클래스를 정의하고, 상속으로 출력하는 프로그램을 만드는 과제가 주어졌습니다.
Animal
이라는 기본 클래스를 정의하고, 상속받는 동물 클래스를 구현합니다.
예) Dog, Cat, Cow
Animal
클래스에는 makeSound()
라는 순수 가상 함수를 포함합니다.
상속 받은 동물에서 makeSound()
를 재정의하고, 동물의 소리를 출력합니다.
랜덤으로 동물 객체를 반환하는 함수를 구현합니다.
Animal
객체의 포인터를 받아 포인터 배열에 저장하는 함수를 구현합니다.
동물원에 있는 모든 동물의 소리를 순차적으로 내는 함수를 구현합니다.
모든 동물 객체의 메모리를 해제하는 소멸자를 구현합니다.
메모리를 올바르게 해제하는지 확인한다.
zoo
클래스 내에 animals
배열이 있으며, 배열에 새로운 항목을 추가할 때, 제한된 크기를 고려하여 배열의 크기를 동적으로 확장하거나 적절한 예외 사항을 반영한다.
#include <iostream>
#include <cstdlib>
#include <ctime>
// CH2 OOP Summary 2번 필수 및 도전과제 제출
// 순수 가상 함수를 가진 추상클래스 Animal
// 생성자는 부모 클래스부터 호출되고, 자식 클래스가 나중에 호출된다.
// 소멸자는 자식 클래스부터 호출되고, 부모 클래스가 나중에 호출된다.
class Animal
{
public:
virtual void makeSound() const = 0;
};
// Animal을 상속받는 클래스들
// makeSound()를 재정의
class Dog : public Animal
{
virtual void makeSound() const override
{
std::cout << "개 : Woof" << std::endl;
}
};
class Cat : public Animal
{
virtual void makeSound() const override
{
std::cout << "고양이 : Meow" << std::endl;
}
};
class Cow : public Animal
{
virtual void makeSound() const override
{
std::cout << "소 : Moo" << std::endl;
}
};
class Zoo
{
private:
Animal** animals; // 동물 객체를 저장하는 포인터 배열
int animalCount = 0;
int arraysize = 0;
public:
int getAnimalCount() { return animalCount; }
// 동물을 동물원에 추가하는 함수
// - Animal 객체의 포인터를 받아 포인터 배열에 저장합니다.
// - 같은 동물이라도 여러 번 추가될 수 있습니다.
// - 입력 매개변수: Animal* (추가할 동물 객체)
// - 반환값: 없음
void addAnimal(Animal* animal)
{
if (animalCount == arraysize)
{
(arraysize == 0) ? arraysize = 1 : arraysize *= 2;
Animal** newAnimals = new Animal * [arraysize];
for (int i = 0; i < animalCount; ++i)
{
newAnimals[i] = animals[i];
}
delete[] animals;
animals = newAnimals;
}
animals[animalCount++] = animal;
}
// 동물원에 있는 모든 동물의 행동을 수행하는 함수
// - 모든 동물 객체에 대해 순차적으로 소리를 내고 움직이는 동작을 실행합니다.
// - 입력 매개변수: 없음
// - 반환값: 없음
void performActions()
{
for (int i = 0; i < animalCount; ++i)
{
animals[i]->makeSound();
}
}
// Zoo 소멸자
// - Zoo 객체가 소멸될 때, 동물 벡터에 저장된 모든 동물 객체의 메모리를 해제합니다.
// - 메모리 누수를 방지하기 위해 동적 할당된 Animal 객체를 `delete` 합니다.
// - 입력 매개변수: 없음
// - 반환값: 없음
~Zoo()
{
for (int i = 0; i < animalCount; ++i)
{
delete animals[i];
}
delete[] animals;
}
};
// 랜덤 동물을 생성하는 함수
// - 0, 1, 2 중 하나의 난수를 생성하여 각각 Dog, Cat, Cow 객체 중 하나를 동적으로 생성합니다.
// - 생성된 객체는 Animal 타입의 포인터로 반환됩니다.
// - 입력 매개변수: 없음
// - 반환값: Animal* (생성된 동물 객체의 포인터)
Animal* createRandomAnimal()
{
Animal* makeAnimal = nullptr;
int curRand = rand() % 3;
switch (curRand)
{
case 0:
makeAnimal = new Dog;
std::cout << "개 1마리가 추가됐습니다." << std::endl;
break;
case 1:
makeAnimal = new Cat;
std::cout << "고양이 1마리가 추가됐습니다." << std::endl;
break;
case 2:
makeAnimal = new Cow;
std::cout << "소 1마리가 추가됐습니다." << std::endl;
break;
default:
break;
}
return makeAnimal;
}
int main()
{
// 필수
Dog dog1;
Cat cat1;
Cow cow1;
// 포인터 배열
Animal* animals[3]{ &dog1, &cat1, &cow1 };
// 객체 순회 및 소리 출력
for (int i = 0; i < 3; ++i)
{
animals[i]->makeSound();
}
// 도전
srand(time(0));
Zoo Zoo1;
bool bInputLoop = true;
while (true == bInputLoop)
{
int inputValue = 0;
std::cout << "동물을 추가하시려면 1을 입력해주세요" << std::endl;
std::cout << "현재 " << Zoo1.getAnimalCount() << " 마리가 추가됐습니다." << std::endl;
std::cout << "더이상 추가하지 않으려 하신다면 다른 값을 입력해주세요" << std::endl;
std::cin >> inputValue;
if (1 != inputValue)
{
bInputLoop = false;
continue;
}
Zoo1.addAnimal(createRandomAnimal());
}
Zoo1.performActions();
}
추상 클래스의 포인터 배열을 만들 수 없어서 포인터포인터 배열을 만들어 동적 배열을 사용했습니다.
동물 객체가 동적으로 생성되며, 포인터로 관리를 하기 때문에 포인터포인터 배열을 사용해도 문제가 없었습니다.
구현코드는 아래 링크의 깃허브에서 직접 볼 수 있습니다.
Sparta_CH_2
댓글남기기