2024-12-23_TIL

별빛에소원을·2024년 12월 23일

TeamSparta-Unreal1기-TIL

목록 보기
14/90
post-thumbnail

C++ 공부

다이아몬드 출력 !


포문을 여러번 쓰기가 싫어서 생각보다 오래 걸렸는데
출력되는 라인을 컨트롤해서 구현하는데 성공하였다.

	char Space = ' ';
	char Star = '*';
	int Half = iNum-1, LineCount = Half +  iNum , SpaceCount,StarCount;
	for (int Line = 0; Line < LineCount; ++Line)
	{
		int Index = Line;
		if (Line > Half)
		{
			Index = LineCount - Line - 1;			
		} 		
		//공백출력
		for (int i = 0; i < Half-Index; ++i)
		{
			std::cout << Space;
		}
		StarCount = Index * 2 + 1;

		//별출력		
		for (int j = 0; j < StarCount; ++j)
		{
			std::cout << Star;
		}
		std::cout << "\n";
	}

line 1는 공백 3개, 별 1개
line 2는 공백 2개, 별 2개
line 3는 공백 1개, 별 5개다
공백은 하나씩 줄어들고, 별은 2개씩 증가한다.
라인을 결정할 수 있는 거는 중간 이후와 이전으로 구분하게 되면, Line을 천천히 올리다가 중간을 넘어가면 전체 Count에 Line을 빼주면 Line을 컨트롤하는 Index변수는 1,2,3와 3,2,1 이렇게 결정이 된다 Index에 따라서 공백을 출력하게 된다

별은 Line이 0일때 1, Line이 1일때 3 2씩 증가하게 된다.
별은 Index는 0,1,2 씩 올라가게 되고 그 떄마다 1부터 2씩 증가하게 되므로,
0 2 + 1 , 1 2 + 1, 이런식으로 다시 정리할 수 있다.
StarCount = Index * 2 + 1하면 정상적으로 숫자를 바뀌어도 정상적으로 출력이 된다.


Class

클래스와 기능 분리

모든 코드가 한 파일안에 들어있으면 유지보수가 매우 어렵게 된다.
기능을 담당하는 파일이 있다면 유지보수도 쉬워지고 직관적으로 해당 Class의 기능을 파악할 수 있게 되므로 복잡한 프로그램을 구조적으로 짤 수 있게 된다.

베터리 Class 만들기

시나리오는 다음과 같다 베터리의 초기값은 100%이고
베터리를 사용하면 5% 소비하고, 충전하면 7% 충전된다.
해당 내용을 가지고 다음과 같이 구성하였다.

Bettery.h

class CBettery
{
public:
	CBettery(int Charge=100);
public:
	const int GetBattery() const;
	void UseBattery();
	void ChargeBattery();
	void PrintRemainingPower() const;
private:
	int m_Battery = 0;
};

함수는 기능을 정의한다.

  • 베터리를 가져오는 기능
  • 베터리를 사용하는 기능
  • 베터리를 충전하는 기능
  • 베터리 값을 출력하는 기능

구현부는 다음과 같이 구현하였다.

Bettery.cpp

CBettery::CBettery(int Charge) :
    m_Battery(Charge)
{
    std::cout << "충전 초기화 : " << 100 << "%" << std::endl;
}

const int CBettery::GetBattery() const
{
    return m_Battery;
}

void CBettery::UseBattery()
{
    if (m_Battery == 0)
    {
        std::cout << "베터리를 충전하세요" << std::endl;
        return;
    }

    m_Battery -= 5;

    if (m_Battery < 0)
        m_Battery = 0;

    std::cout << "베터리를 사용했습니다. 남은 잔량 : " << m_Battery << "%"<<std::endl;
}

void CBettery::ChargeBattery()
{
    if (m_Battery == 100)
    {
        m_Battery = 100;
        std::cout << "베터리가 모두 충전되었습니다." << std::endl;
        return;
    }

    m_Battery += 7;

    if (m_Battery > 100)
        m_Battery = 100;

    std::cout << "베터리를 충전했습니다. 남은 잔량 : " << m_Battery << "%" << std::endl;
}

void CBettery::PrintRemainingPower() const
{
    std::cout << "남은 베터리 잔량 : " << m_Battery << std::endl;
}

분수를 클래스로 구현하자

예제 문제를 잘못 이해해서 시간이 생각보다 많이걸렸다.
최대공약수와 최소공배수를 만드는 기능이 핵심 기능이고, 분수의 연산부는 Operator로 구현하였다.
알고리즘 생각하는게 부족한거 같다 많이 많이 연습해두자

Fraction.h

class CFraction
{
public:
	CFraction();
	CFraction(int Numerator, int Denominator);
	~CFraction();
private:
	// 분자
	int m_Numerator=1;
	// 분모
	int m_Denominator=1;
public:
	void SetFountain(const int Numerator, const int Denominator);
	void PrintDisplay();
public:
	void operator= (const CFraction& Fountain);
	CFraction operator* (const int& num);
	void operator*= (const int& num);
	CFraction operator* (const CFraction& Fountain);
	void operator*= (const CFraction& Fountain);
	CFraction operator+ (const CFraction& Fountain);
	void operator+= (const CFraction& Fountain);
	CFraction operator- (const CFraction& Fountain);
	void operator-= (const CFraction& Fountain);
private:
	int GreatestCommonDivisor(const int& Nemerator,const int& Denominator);
	int CommonMultiple(const int& Denominator);
	void Simplify();
};

분자와 분모를 맴버변수로 가지고 있고 나머지는 연산자오버로딩을 통해 연산된다.
헥심기능인 최대공약수와 최대공배수를 구하는 로직은 외부에서 접근할 이유가 없기에 private로 구현하였다.

Fraction.cpp

CFraction::CFraction()
{
    m_Numerator = 0;
    m_Denominator = 0;
}

CFraction::CFraction(int Numerator, int Denominator) :
    m_Numerator(Numerator),
    m_Denominator(Denominator)
{
}

CFraction::~CFraction()
{
}

void CFraction::SetFountain(const int Numerator, const int Denominator)
{
    m_Numerator = Numerator;
    m_Denominator = Denominator;
}

void CFraction::Simplify()
{
    int gcd = GreatestCommonDivisor(m_Numerator, m_Denominator);

    m_Numerator /= gcd;
    m_Denominator /= gcd;
}

void CFraction::PrintDisplay()
{
    std::cout << m_Numerator << "/" << m_Denominator << std::endl;
}
void CFraction::operator= (const CFraction& fraction)
{
    m_Numerator = fraction.m_Numerator;
    m_Denominator = fraction.m_Denominator;
}
CFraction CFraction::operator*(const int& num)
{
    CFraction Result(m_Numerator, m_Denominator);

    Result.m_Denominator *= num;
    Result.m_Numerator *= num;

    return Result;
}
void CFraction::operator*=(const int& num)
{
    m_Numerator *= num;
    m_Denominator *= num;
}
CFraction CFraction::operator*(const CFraction& fraction)
{    
    CFraction Result(fraction.m_Numerator * m_Numerator, fraction.m_Denominator * m_Denominator);
    Result.Simplify();
    return Result;
}

void CFraction::operator*=(const CFraction& fountain)
{
    m_Numerator *= fountain.m_Numerator;
    m_Denominator *= fountain.m_Denominator;
    Simplify();
}

CFraction CFraction::operator+(const CFraction& fraction)
{
    int lcm = CommonMultiple(fraction.m_Denominator);

    CFraction Factor;
    int multiple;

    Factor.m_Denominator = lcm;
    multiple = lcm / m_Denominator;
    Factor.m_Numerator += m_Numerator * multiple;

    multiple = lcm / fraction.m_Denominator;
    Factor.m_Numerator += fraction.m_Numerator * multiple;

    return Factor;
}

void CFraction::operator+=(const CFraction& fraction)
{
    int lcm = CommonMultiple(fraction.m_Denominator);

    CFraction Factor;
    int multiple;

    Factor.m_Denominator = lcm;
    multiple = lcm / m_Denominator;
    Factor.m_Numerator += m_Numerator * multiple;

    multiple = lcm / fraction.m_Denominator;
    Factor.m_Numerator += fraction.m_Numerator * multiple;

    m_Numerator = Factor.m_Numerator;
    m_Denominator = Factor.m_Denominator;
}

CFraction CFraction::operator-(const CFraction& fraction)
{
    int lcm = CommonMultiple(fraction.m_Denominator);

    CFraction Factor;
    int multiple;

    Factor.m_Denominator = lcm;
    multiple = lcm / m_Denominator;
    Factor.m_Numerator += m_Numerator * multiple;

    multiple = lcm / fraction.m_Denominator;
    Factor.m_Numerator -= fraction.m_Numerator * multiple;

    return Factor;
}

void CFraction::operator-=(const CFraction& fraction)
{
    int lcm = CommonMultiple(fraction.m_Denominator);

    CFraction Factor;
    int multiple;

    Factor.m_Denominator = lcm;
    multiple = lcm / m_Denominator;
    Factor.m_Numerator += m_Numerator * multiple;

    multiple = lcm / fraction.m_Denominator;
    Factor.m_Numerator -= fraction.m_Numerator * multiple;

    m_Numerator = Factor.m_Numerator;
    m_Denominator = Factor.m_Denominator;
}

int CFraction::GreatestCommonDivisor(const int& Nemerator, const int& Denominator)
{
    if (Nemerator == 0 || Denominator == 0)
        return 1;

    int Measure = Denominator;

    int gcd = Nemerator;
    while (Measure != 0)
    {  
        int temp = Measure;
        Measure = gcd % Measure;
        gcd = temp;
    }
    return gcd;
}

int CFraction::CommonMultiple(const int& Denominator)
{
    if (m_Denominator == 0 || Denominator == 0)
        return 1;
    // 최소공배수는 A*B / GCD(A,B)와 같다.
    int MeasureA = m_Denominator; // 
    int MeasureB = Denominator;
    int gcd = GreatestCommonDivisor(m_Denominator, Denominator);
    int result = (MeasureA * MeasureB) / gcd;
    return result;
}

코드가 긴거 같지만 핵심 로직은 단순하다 최대공약수는 계속 나머지가 0이 될때까지 나누어서 0이되면 그 값이 최대공약수가 된다.
최대공배수는 단순이 곱해서 최대 공약수로 나누게 되면 구할 수있다.
최대공약수를 구해 놓으면 다양한 방법으로 분수의 덧셈도 뺄셈도 최대공배수도 구현하기 편해서 구현해 놓았다.

다형성

다형성은 클래스의 기능확장인 상속기능을 통해 구현할 수 있다.
가상함수를 통해 상속받은 클래스에서 재정의 해서 사용하는것이 핵심 기능이다.
상속용도로 가상함수만 정의해놓은 클래스를 추상클래스라고 한다.
가상함수는 내부적으로 메모리가 할당되어 참조영역을 지정하는 구조이다.

다음과 같이 클래스를 디자인해보자.

다음과 같이 구현하였다.

Adventure.h

class CAdventure
{
public:
	CAdventure(){}
	~CAdventure(){}
public:
	virtual void UseSkill(void) = 0;
};

Archer.h

class CArcher :
    public CAdventure
{
public:
    CArcher();
    ~CArcher();
public:
    void UseSkill() override;
};

Mage.h

class CMage :
    public CAdventure
{
public:
    CArcher();
    ~CArcher();
public:
    void UseSkill() override;
};

Warror.h

class CWarror :
    public CAdventure
{
public:
    CArcher();
    ~CArcher();
public:
    void UseSkill() override;
};

main.cpp

	std::vector<std::shared_ptr<CAdventure>> vecCharacter;	
	vecCharacter.push_back(std::make_shared<CMage>());
	vecCharacter.push_back(std::make_shared<CArcher>());
	vecCharacter.push_back(std::make_shared<CWarror>());

	size_t size = vecCharacter.size();

	for (size_t i = 0; i < size; ++i)
	{
		vecCharacter[i].get()->UseSkill();
	}

효과적으로 다형성을 이용하기 위해서 컨테이너에 클래스를 저장해놓고 사용하는게 효율적인데 포인터를 안전장치 없이 사용하는 것 보다는 스마트포인터를 사용하는 것이 안전하기에 습관을 들이고자 선택하였다. unique_ptr, weak_ptr도 언제 어떠한 때 사용하면 좋은지 공부해보자.

profile
취미로 게임하는사람

0개의 댓글