CPP Module 03 배 긁으면서 해결하기

정하나둘·2023년 6월 18일
0
post-thumbnail

여기까지 온 당신 그동안 norminette규정 준수하고 함수도 마음껏 사용하지 못하셨죠?...
이젠 행복해질 수 있습니다.
cpp과제부터는 norm규정이 없어지고 함수 제한도 없어서 당신의 꿈과 희망을 맘껏 펼칠 수 있습니다.
이제 시작해보도록 하겠습니다.

규정

  • 당신의 코드는 c++과 -Wall -Wextra -Werror 플래그로 컴파일 되어야 합니다.
  • 당신의 코드는 -std=c++98 플래그를 추가해도 여전히 컴파일 되어야 합니다.
  • 디렉토리는 ex00, ex01, ex02로 이름지어져야 합니다.
    파일, 클래스명, 함수명, 멤버 함수, 속성은 가이드라인에 맞춰 이루어져야 합니다.
  • 클래스명은 UpperCamelCase 형식으로 작성되어야 합니다. 클래스를 포함한 파일들은 항상 클래스 이름에 따라 이름이 지정됩니다.
    For instance:
    ClassName.hpp/ClassName.h, ClassName.cpp, or ClassName.tpp.
    brick wall을 나타내는 BrickWall 클래스의 정의를 포함하는 헤더 파일이 있는 경우 이 이름은 BrickWall.hpp가 됩니다.
  • 달리 지정하지 않는 경우, 모든 출력 메세지는 개행으로 끝나야 합니다.
  • norm 규정을 지키지 않아도 됩니다! 다만 당신이 코드를 평가자가 이해할 수 없게 작성한다면 평가를 진행할 수 없습니다. 깨끗하고 읽기 쉬운 글을 작성하기 위해 노력해주세요.
  • cpp 표준 라이브러리의 거의 모든 것을 사용할 수 있습니다. 따라서 이미 알고 있는 것에 집착하는 대신 익숙한 C 함수의 C++ 버전을 많이 사용하는게 좋을겁니다!
  • 하지만 다른 외부 라이브러리는 사용할 수 없습니다. 이는 C++11 및 Boost 라이브러리가 금지됨을 의미합니다. 다음 함수도 금지됩니다 : printf(), alloc(), free(). 해당 함수들을 사용할 경우 0점 입니다 ^^.
  • 별도로 명시하지 않는 한 unsing namespace <ns_name> 및 friend 키워드는 금지됩니다. 쓰시면 -42점이에요 ^^.
  • cpp module 08에서만 STL을 사용할 수 있습니다. 즉, 그때까지는 컨테이너(vector/list/map/등등)과 알고리즘 (algorithm)헤더를 포함해야 하는 모든 것이 허용되지 않습니다. 쓰시면 -42점이에요 ^^.
  • cpp에서도 메모리 누수는 발생합니다. 반드시 누수 잡아주세요.
  • module02부터 8까지 당신의 모든 클래스들은 반드시 Orthodox Canonical Form에 맞춰 작성되어야 합니다.(달리 명시된 경우 제외)
  • 헤더 파일에 있는 모든 함수 구현(함수 템플릿 제외)은 연습이 안됩니다.
  • 각 헤더들을 독립적으로 사용할 수 있어야 합니다. 따라서 헤더들은 반드시 그들이 필요한 종속성을 포함하고 있어야 합니다. 그러나 반드시 가드 추가로 인한 이중 침입 충돌을 피해야 합니다. 안그럼 0점이에요^^.
  • 필요한 경우(예: 코드 분할) 몇 가지 추가 파일을 추가 할 수 있습니다. 기계체점이 아니므로 자유롭게 하세요.
  • 경우에 따라 과제 지침이 짧아 보이지만 예제는 지침에 명시적으로 작성되지 않은 요구 사항을 보여줄 수 있습니다.
  • 시작하기 전에 각 모듈을 읽으세요 제발!
  • 머리를 쓰세요^^

이번 과제는 상속(inheritance)에 관련된 문제다.

과제에 들어가기 앞서 상속에 대해 조금 알아보자

새로 확장된 클래스가 기존에 존재하던 클래스의 멤버를 물려받는 것을 상속이라고 한다.
이때, 바탕이 되는 클래스를 기본 클래스(base class), 새로운 클래스를 파생 클래스(derived class)라고 부른다.

기본 클래스와 파생 클래스는 아래에서 과제를 진행하며 볼 수 있으니 파생 클래스를 어떻게 만드는지 정도만 알고 넘어가도 좋을 것 같다.

파생 클래스 선언

class 파생 클래스명 : 접근 지정자 기본 클래스명
{
	파생 클래스에 추가할 멤버 선언
}

ex:
-기본 클래스-
class Car
{
	private:
    	int num;
        double gas;
    public:
    	Car();
        void setCar(int n, double g);
        void show();
};

-파생 클래스-
class RacingCar : public Car
{
	private:
    	int course;
    public:
    	RacingCar();
       	void setCourse(int c);
};

파생 클래스는 해당 방법으로 생성할 수 있고 기본 클래스에서 지정자가 어떻게 선언되어있고 파생 클래스에서 상속 접근 지정자를 어떻게 선언하냐에 따라 상속 형태는 다음처럼 달라진다.

  1. 먼저 기본 클래스의 정의부에서 public으로 정의된 속성에 대해서는 파생 클래스의 정의부에서 접근이 가능하며 외부에서도 접근이 가능하다.
  2. 기본 클래스의 정의부에서 private로 정의된 속성에 대해서는 파생 클래스의 정의부에서 접근이 불가능하며 파생클래스의 객체를 통해서도 접근이 불가능하다. 오직 기본 클래스의 정의부 내에서만 접근이 가능하다.
  3. 기본 클래스의 정의부에서 protected로 정의된 속성에 대해서는 파생클래스의 정의부에서 접근이 가능하지만 외부에서는 접근이 불가능하다.

이것만 알아도 해당 과제는 충분히 해결할 수 있고 다른 내용들은 CPP 과제 후반부로 갈수록 더 나오는 것으로 알고 있어 기본 지식 설명은 이정도 하고 넘어가도록 하자.

ex00

클랩트랩 클래스를 만들면 되는 간단한 과제다.
private:
생성자 매개변수로 초기화하는 name
HitPoint(10)
Energy points(10)
Attack damage(0)

public:
void attack(const std::string& target);
void takeDamage(unsigned int amount);
void beRepaired(unsigned int amount);

attack 호출 시 target의 hp가 damage만큼 줄어들고
beRepaired 호출 시 해당 claptrap의 hp가 amount만큼 늘어나며,
attack, beRepaired 함수 모두 호출 시 energy points가 1씩 감소한다.
hp나 energy point가 남아있지 않으면 아무것도 할 수 없고
모든 멤버 함수들이 호출될 때 메세지가 출력되어야한다.(생성자 소멸자 포함)

ClapTrap.hpp

#ifndef CLAPTRAP_HPP
#define CLAPTRAP_HPP

#include <iostream>

class ClapTrap{
	private:
		std::string	Name;
		unsigned int	hit_p;
		unsigned int	energy_p;
		unsigned int	attack_d;
	public:
		ClapTrap();
		ClapTrap(std::string name);
		ClapTrap(const ClapTrap& obj);
		ClapTrap&	operator=(const ClapTrap& obj);
		~ClapTrap();
		void	attack(const std::string& target);
		void	takeDamage(unsigned int amount);
		void	beRepaired(unsigned int amount);
};

#endif

ClapTrap.cpp

#include "ClapTrap.hpp"

ClapTrap::ClapTrap()
{
	this->Name = "Default";
	this->hit_p = 10;
	this->energy_p = 10;
	this->attack_d = 0;
	std::cout << "ClapTrap default constructor called" << std::endl;
}

ClapTrap::ClapTrap(std::string name)
{
	this->Name = name;
	this->hit_p = 10;
	this->energy_p = 10;
	this->attack_d = 0;
	std::cout << "ClapTrap "<< name << " constructor called" << std::endl;
}

ClapTrap::ClapTrap(const ClapTrap& obj)
{
	this->Name = obj.Name;
	this->attack_d = obj.attack_d;
	this->energy_p = obj.energy_p;
	this->hit_p = obj.hit_p;
	std::cout << "ClapTrap " << this->Name << " copy constructor called" << std::endl;
}

ClapTrap& ClapTrap::operator=(const ClapTrap& obj)
{
	if (this != &obj)
	{
		this->Name = obj.Name;
		this->attack_d = obj.attack_d;
		this->energy_p = obj.energy_p;
		this->hit_p = obj.hit_p;
		std::cout << "ClapTrap operator = " << this->Name << " called" << std::endl;
	}
	return (*this);
}

ClapTrap::~ClapTrap()
{
	std::cout << "ClapTrap" << this->Name << "destructor called" << std::endl;
}

void	ClapTrap::attack(const std::string &target)
{
	if (this->hit_p == 0 && this->energy_p == 0)
	{
		std::cout << "ClapTrap " << this->Name << "can't move" << std::endl;
	}
	else
	{
		std::cout << "ClapTrap " << this->Name << " attacks " << target << ", causing " << this->attack_d << " points of damage!" << std::endl;
		this->energy_p--;
	}
}

void	ClapTrap::takeDamage(unsigned int amount)
{
	if (amount > this->hit_p)
		this->hit_p = 0;
	else
		this->hit_p -= amount;
	std::cout << "ClapTrap " << this->Name << " has taken " << amount << " damage" << std::endl;
	if (this->hit_p == 0)
		std::cout << "ClapTrap " << this->Name << " is died" << std::endl;
}

void	ClapTrap::beRepaired(unsigned int amount)
{
	if (this->hit_p == 0 || this->energy_p == 0)
	{
		std::cout << "ClapTrap " << this->Name << " can't move" << std::endl;
	}
	else
	{
		std::cout << "ClapTrap " << this->Name << "'s hp is repaired!" << std::endl;
		this->hit_p += amount;
		this->energy_p--;
	}
}

main.cpp

#include "ClapTrap.hpp"

#include "ClapTrap.hpp"

int	main(void)
{
	ClapTrap a("A");
	ClapTrap b("B");

	a.attack("B");	//a의 b공격
	b.takeDamage(5);	//b가 a의 attack damage만큼 피해를 입음
	b.beRepaired(3);	//b가 체력을 회복
	b.attack("A");
	a.takeDamage(10);
	a.beRepaired(10);
	return (0);
}

ex00은 그저 OCF에 맞춰 클래스 하나를 만들어주면 되는 과제라 굉장히 간단했다.
ex01부터 상속에 대한 내용이 나오니 ex01로 얼른 넘어가자.


ex01

ex00에서 만든 claptrap을 상속받은 scavtrap을 구현하면 된다.
생성자와 소멸자, attack()은 다른 메세지를 출력해야하며
scavtrap이 생성될 때, claptrap이 먼저 생성되지만 소멸할 땐 scavtrap이 먼저 소멸되고 그 다음 claptrap이 소멸되는데 그 이유를 알고 있어야 한다.

이름(생성자에 매개변수로 전달)
HP(100)
Energy Point(50)
Damage(20)

  • void guardGate(); 해당 함수는 ScavTrap의 현재 gatekeeper mode 상태를 메세지로 나타내야한다. (뭔 소린지 몰랐지만 그냥 경계모드 활성화 정도로 이해하고 구현했다)

ClapTrap.hpp

#ifndef CLAPTRAP_HPP
#define CLAPTRAP_HPP

#include <iostream>

class ClapTrap{
	protected:		//상속을 위해 지정자를 private에서 protected로 바꿔줬다.
		std::string	Name;
		unsigned int	hit_p;
		unsigned int	energy_p;
		unsigned int	attack_d;
	public:
		ClapTrap();
		ClapTrap(std::string name);
		ClapTrap(const ClapTrap& obj);
		ClapTrap&	operator=(const ClapTrap& obj);
		~ClapTrap();
		void	attack(const std::string& target);
		void	takeDamage(unsigned int amount);
		void	beRepaired(unsigned int amount);
};

#endif

ScavTrap.hpp

#ifndef SCAVTRAP_HPP
#define SCAVTRAP_HPP

# include <iostream>
# include "ClapTrap.hpp"

class ScavTrap : public ClapTrap	//상속 클래스 생성
{
	public:
		ScavTrap(void);
		ScavTrap(std::string name);
		ScavTrap(const ScavTrap& obj);
		ScavTrap& operator=(const ScavTrap& obj);
		~ScavTrap(void);
		void	attack(const std::string& target);
		void	takeDamage(unsigned int amount);	//얘랑
		void	beRepaired(unsigned int amount);	//얘는 굳이 만들 필요 없다. 뒤에서도 쓸모가 딱히 없었다... 하지만 그냥 main문에서 사용할 때 같은 속성이여야 하지 않을까? 싶어서 만들었었다...
		void	guardGate(void);
};

#endif

ScavTrap.cpp

#include "ScavTrap.hpp"

ScavTrap::ScavTrap(void)
{
	this->hit_p = 100;
	this->energy_p = 50;
	this->attack_d = 20;
	std::cout << "ScavTrap default constructor called" << std::endl;
}

ScavTrap::ScavTrap(std::string name)
{
	this->Name = name;
	this->hit_p = 100;
	this->energy_p = 50;
	this->attack_d = 20;
	std::cout << "ScavTrap " << name << " constructor called" << std::endl;
}

ScavTrap::ScavTrap(const ScavTrap& obj)
{
	this->Name = obj.Name;
	this->hit_p = obj.attack_d;
	this->energy_p = obj.energy_p;
	this->attack_d = obj.attack_d;
	std::cout << "ScavTrap " << this->Name << " copy constructor called" << std::endl;
}

ScavTrap& ScavTrap::operator=(const ScavTrap& obj)
{
	if (this != &obj)
	{
		this->Name = obj.Name;
		this->hit_p = obj.hit_p;
		this->energy_p = obj.energy_p;
		this->attack_d = obj.attack_d;
		std::cout << "ScavTrap operator = " << this->Name << " called" << std::endl;
	}
	return (*this);
}

ScavTrap::~ScavTrap(void)
{
	std::cout << "ScavTrap " << this->Name << " destructor called" << std::endl;
}

void	ScavTrap::attack(const std::string& target)
{
	if (this->hit_p == 0 && this->energy_p == 0)
	{
		std::cout << "ScavTrap " << this->Name << "can't move" << std::endl;
	}
	else
	{
		std::cout << "ScavTrap " << this->Name << " attacks " << target << ", causing " << this->attack_d << " points of damage!" << std::endl;
		this->energy_p--;
	}
}

void	ScavTrap::takeDamage(unsigned int amount)
{
	if (amount > this->hit_p)
		this->hit_p = 0;
	else
		this->hit_p -= amount;
	std::cout << "ScavTrap " << this->Name << " has taken " << amount << " damage" << std::endl;
	if (this->hit_p == 0)
		std::cout << "ScavTrap " << this->Name << " is died" << std::endl;
}

void	ScavTrap::beRepaired(unsigned int amount)
{
	if (this->hit_p == 0 || this->energy_p == 0)
	{
		std::cout << "ScavTrap " << this->Name << " can't move" << std::endl;
	}
	else
	{
		this->hit_p += amount;
		this->energy_p--;
		std::cout << "ScavTrap " << this->Name << " has been repaired of " << amount << "Hit points. It has now " << this->hit_p << std::endl;
	}
}

void	ScavTrap::guardGate(void)
{
	if (this->hit_p == 0|| this->energy_p == 0)
		std::cout << "ScavTrap " << this->Name << " can't move" << std::endl;
	else
		std::cout << "ScavTrap " << this->Name << " has entered gate guard mode" << std::endl;
}

앞에서 말했던 상속에 대한 것만 잘 이해했다면 어려운 과제가 아니다. 그냥 멤버를 물려받고 그 멤버들을 가지고 노는 형태라 어렵진 않았다.

생성자와 소멸자의 출력 순서에 관련해서는
상속을 받을 때 claptrap의 데이터를 가진 scavtrap이 생성되는게 아니라 claptrap이 먼저 생성되고 그 주소를 가진 scavtrap이 생성되기 때문에 기본 클래스의 생성자가 출력되고 그 다음 파생 클래스의 생성자가 출력되는 것이고 소멸할 때는 반대로 파생 클래스가 먼저 소멸하고 기본 클래스가 소멸되기 때문에 그렇다.
만약 파생 클래스가 먼저 생성되고 그 다음 기본 클래스가 생성되는 것이라면 claptrap의 변수를 scavtrap에서 초기화하기 불편할 것이며 claptrap이 먼저 소멸된다면 scavtrap의 소멸자에서 기본 클래스의 함수나 변수를 사용할 수 없기 때문에 해당 순서가 정해진 것으로 보인다.


ex02

이번에는 fragtrap을 만드는데 ex01과 유사하며 attack을 따로 만들지 않아도 되고 guardgate()대신 highfivesguys()를 구현하면 된다. attack을 따로 만들지 않아도 된다고 했지만 fragtrap 객체에서 출력문에 claptrap이 나오는게 너무 불편해 도저히 그냥 보고 있을 수 없어서 나는 다 만들었다...

FragTrap.hpp

#ifndef FRAGTRAP_HPP
# define FRAGTRAP_HPP

# include <iostream>
# include "ClapTrap.hpp"

class FragTrap : public ClapTrap
{
	public:
		FragTrap(void);
		FragTrap(std::string name);
		FragTrap(const FragTrap& obj);
		FragTrap& operator=(const FragTrap& obj);
		~FragTrap(void);
		void	attack(const std::string& target);
		void	takeDamage(unsigned int amount);
		void	beRepaired(unsigned int amount);
		void	highFivesGuys(void);
};

# endif

FragTrap.cpp

#include "FragTrap.hpp"

FragTrap::FragTrap(void)
{
	this->hit_p = 100;
	this->energy_p = 100;
	this->attack_d = 30;
	std::cout << "FragTrap default constructor called" << std::endl;
}

FragTrap::FragTrap(std::string name)
{
	this->Name = name;
	this->hit_p = 100;
	this->energy_p = 100;
	this->attack_d = 30;
	std::cout << "FragTrap " << name << " constructor called" << std::endl;
}

FragTrap::FragTrap(const FragTrap& obj)
{
		this->Name = obj.Name;
		this->hit_p = obj.hit_p;
		this->energy_p = obj.energy_p;
		this->attack_d = obj.attack_d;
		std::cout << "FragTrap " << this->Name << " copy constructor called" << std::endl;
}

FragTrap& FragTrap::operator=(const FragTrap& obj)
{
	if (this != &obj)
	{
		this->Name = obj.Name;
		this->hit_p = obj.hit_p;
		this->energy_p = obj.energy_p;
		this->attack_d = obj.attack_d;
		std::cout << "FragTrap operator = " << this->Name << " called" << std::endl;
	}
	return (*this);
}

FragTrap::~FragTrap(void)
{
	std::cout << "FragTrap " << this->Name << " destructor called" << std::endl;
}

void	FragTrap::attack(const std::string& target)
{
	if (this->energy_p == 0 || this->hit_p == 0)
	{
		std::cout << "FragTrap " << this->Name << " can't move" << std::endl;
	}
	else
	{
		std::cout << "FragTrap " << this->Name << " attacks " << target << ", causing " << this->attack_d << " points of damage!" << std::endl;
		this->energy_p--;
	}
}

void	FragTrap::takeDamage(unsigned int amount)
{
	if (amount > this->hit_p)
		this->hit_p = 0;
	else
		this->hit_p -= amount;
	std::cout << "FragTrap " << this->Name << " has taken " << amount << " damage" << std::endl;
	if (this->hit_p == 0)
		std::cout << "FragTrap " << this->Name << " is died" << std::endl;
}

void	FragTrap::beRepaired(unsigned int amount)
{
	if (this->hit_p == 0 || this->energy_p == 0)
	{
		std::cout << "FragTrap " << this->Name << " can't move" << std::endl;
	}
	else
	{
		this->hit_p += amount;
		this->energy_p--;
		std::cout << "FragTrap " << this->Name << " has been repaired of " << amount << "Hit points. It has now " << this->hit_p << std::endl;
	}
}

void	FragTrap::highFivesGuys(void)
{
	if (this->energy_p == 0)
		std::cout << "FragTrap " << this->Name << " can't ask for a positive high-five because " << this->Name << " is dead" << std::endl;
	else
		std::cout << "FragTrap " << this->Name << " asks for a positive high-five" << std::endl;
}

여기까진 쉬웠는데 ex03은 문제를 읽었을 때 한 번에 팍 와닿지 않아서 생각을 좀 많이 했던 것 같다.

ex03

우선 ex03 과제는 앞에서 만든 최상위 클래스(claptrap)을 상속받는 두 개의 클래스(scavtrap, fragtrap)을 상속받는 diamondtrap을 만드는 과제이다.

diamondtrap을 그냥 일반적인 상속으로 받으면 우리는 오른쪽 사진처럼 된다고 생각하지만 실제로는 왼쪽 이미지처럼 생성된다.

하지만 이렇게되면 classA가 두 개 생성되기 때문에 두 개의 class A를 모두 상속받는 class D의 입장에서는 classA 변수를 하나 찾아가게 되면 둘 중 어디를 가야할지 몰라 모호한 입장이 되기 때문에 컴파일에 문제가 생긴다.
따라서 해당 문제를 해결하기 위해 virtual이라는 가상 기본 상속을 통해 오른쪽 모양처럼 상속 할 수 있다.

예시를 보면서 이해해보자.
출처 :https://hwan-shell.tistory.com/224

#include <iostream>

class A {
public:
    A() { printf("A 생성자\n"); }
    ~A() { printf("A 소멸자\n"); }
    int A_num;
};

class B : public A {
public:
    B() { printf("B 생성자\n"); }
    ~B() { printf("B 소멸자\n"); }
private:
    int B_num;
};

class C : public A {
public:
    C() { printf("C 생성자\n"); }
    ~C() { printf("C 소멸자\n"); }
private:
    int C_num;
};

class D : public B, public C{
public:
    D() { printf("D 생성자\n"); }
    ~D() { printf("D 소멸자\n"); }
private:
    int D_num;
};

int main(void) {

    D d;
    printf("%d\n", sizeof(d));

    return 0;
}


생성자가 호출된 걸 보면 상단 왼쪽 이미지처럼 A생성자가 2번 호출되어있다.
현재 D에 할당되는 메모리 구조를 대략적으로 보면 다음과 같다.

int A_num 변수도 2번 생성되고 생성자 소멸자도 A클래스에 한해 두 번 이뤄졌다.
이제 vitrual 키워드로 가상 기본 상속을 통해 문제를 해결해보자.

#include <iostream>

class A {
public:
    A() { printf("A 생성자\n"); }
    ~A() { printf("A 소멸자\n"); }
    int A_num;
};

class B : virtual public A {
public:
    B() { printf("B 생성자\n"); }
    ~B() { printf("B 소멸자\n"); }
private:
    int B_num;
};

class C : virtual public A {
public:
    C() { printf("C 생성자\n"); }
    ~C() { printf("C 소멸자\n"); }
private:
    int C_num;
};

class D : public B, public C{
public:
    D() { printf("D 생성자\n"); 
    B::A_num = 0;
    }
    ~D() { printf("D 소멸자\n"); 
    }
private:
    int D_num;
};

int main(void) {

    D d;
    printf("%d\n", sizeof(d));

    return 0;
}


불필요한 소멸자와 데이터가 없어졌지만 sizeof(d)로 측정했을 때, 데이터의 크기가 증가한 것을 알 수 있다.

이전과 다르게 vbptr(virtual base table pointer)이 추가되어있다.
vbptr은 int A_num의 위치를 알고있는 offset데이터를 가르키게 된다.
또한 int A_num은 중복을 막기위해 맨 아래로 갔고 이에따라 맨 아래에 위치를 고정시킴으로써 offset정보를 계산할 수 있기 때문이다.
B의 vbptr부분을 보면 시작 offset은 0이고 virtaul base table offset은 20이다.
즉, "0부터 시작하여 20바이트 떨어진 곳에 있다."라고 해석할 수 있다.
시작 offset의 값은 D의 메모리에 적재되는 순간 해당 메모리를 기준으로 계산된다.
복잡하게 offset을 나누는 이유는 몇 개의 상속자가 올 지 모르기 때문이며
예측할 수 없으니 계산을 통해 위치를 알아내려고 만든 것이다.

이렇게 되면 메모리 공간이 커질 수 있으며, offset을 이용한 자료를 찾는 과정 자체가 성능에 영향을 줄 수 있기 때문에 그냥 다이아몬드 상속은 안 쓰는게 좋다. 라는걸 보여주기 위해 만든 과제라고 생각한다.

FragTrap.hpp

#ifndef FRAGTRAP_HPP
# define FRAGTRAP_HPP

# include <iostream>
# include "ClapTrap.hpp"

class FragTrap : public virtual ClapTrap	//virtual 키워드 추가
{
	public:
		FragTrap(void);
		FragTrap(std::string name);
		FragTrap(const FragTrap& obj);
		FragTrap& operator=(const FragTrap& obj);
		~FragTrap(void);
		void	attack(const std::string& target);
		void	takeDamage(unsigned int amount);
		void	beRepaired(unsigned int amount);
		void	highFivesGuys(void);
};

# endif

ScavTrap.hpp

#ifndef SCAVTRAP_HPP
#define SCAVTRAP_HPP

# include <iostream>
# include "ClapTrap.hpp"

class ScavTrap : public virtual ClapTrap
{
	public:
		ScavTrap(void);
		ScavTrap(std::string name);
		ScavTrap(const ScavTrap& obj);
		ScavTrap& operator=(const ScavTrap& obj);
		~ScavTrap(void);
		void	attack(const std::string& target);
		void	takeDamage(unsigned int amount);
		void	beRepaired(unsigned int amount);
		void	guardGate(void);
};

#endif

DiamondTrap.hpp

#ifndef DIAMONDTRAP_HPP
# define DIAMONDTRAP_HPP

#include <iostream>
#include "ClapTrap.hpp"
#include "FragTrap.hpp"
#include "ScavTrap.hpp"

class DiamondTrap : public FragTrap, public ScavTrap	//다중 상속 방법
{
	private:
		std::string Name;
	public:
		DiamondTrap(void);
		DiamondTrap(std::string name);
		DiamondTrap(const DiamondTrap& obj);
		DiamondTrap& operator=(const DiamondTrap& obj);
		~DiamondTrap(void);
		void	attack(const std::string& target);
		void	takeDamage(unsigned int amount);
		void	beRepaired(unsigned int amount);
		void	whoAmI(void);
};

#endif

DiamondTrap.cpp

# include "DiamondTrap.hpp"

DiamondTrap::DiamondTrap(void) : ClapTrap(), FragTrap(), ScavTrap()
{
	this->Name = ClapTrap::Name;
	ClapTrap::Name = Name + "_clap_name";
	std::cout << "DiamondTrap default constructor called" << std::endl;
}

DiamondTrap::DiamondTrap(std::string name) : ClapTrap(), FragTrap(), ScavTrap()
{
	this->Name = name;
	this->attack_d = 30;
	ClapTrap::Name = name + "_clap_name";
	std::cout << "DiamondTrap " << name << " constructor called" << std::endl;
}

DiamondTrap::~DiamondTrap()
{
	std::cout << "DiamondTrap " << this->Name << "destructor called" << std::endl;
}

DiamondTrap::DiamondTrap(const DiamondTrap& obj) : ClapTrap(obj), FragTrap(obj), ScavTrap(obj)
{
	this->Name = obj.Name;
	this->hit_p = obj.hit_p;
	this->energy_p = obj.energy_p;
	this->attack_d = obj.attack_d;
	std::cout << "DiamondTrap " << this->Name << " copy constructor called" << std::endl;
}

DiamondTrap& DiamondTrap::operator=(const DiamondTrap& obj)
{
	this->Name = obj.Name;
	this->attack_d = obj.attack_d;
	this->hit_p = obj.hit_p;
	this->energy_p = obj.energy_p;
	std::cout << "DiamondTrap operator = " << this->Name << "called" << std::endl;
	return (*this);
}

void	DiamondTrap::attack(const std::string& target)
{
	ScavTrap::attack(target);
}

void	DiamondTrap::takeDamage(unsigned int amount)
{
	ScavTrap::takeDamage(amount);
}

void	DiamondTrap::beRepaired(unsigned int amount)
{
	ScavTrap::beRepaired(amount);
}

void	DiamondTrap::whoAmI(void)
{
	std::cout << "DiamondTrap name is " << this->Name << "ClapTrap name is " << ClapTrap::Name << std::endl;
}
profile
내가 다시 보려고 만드는 42서울 본과정 블로그

0개의 댓글