ClapTrap 클래스
attack을 실행할때,
ClapTrap <name> attacks <target>, causing <damage> points of damage!
#include "ClapTrap.hpp"
ClapTrap::ClapTrap() {
this->Name = "default";
std::cout << "ClapTrap_Name : " << this->Name << std::endl;
this->Hitpoints = 10;
this->energy = 10;
this->damage = 0;
}
ClapTrap::ClapTrap(std::string name) {
this->Name = name;
std::cout << "ClapTrap_Name : " << this->Name << std::endl;
this->Hitpoints = 10;
this->energy = 10;
this->damage = 0;
}
ClapTrap::~ClapTrap() {
std::cout << "ClapTrap " << this->Name << " has been destroyed." << std::endl;
}
void ClapTrap::attack(std::string const & target) {
std::cout << "ClapTrap " << this->Name << \
" attacks " << target << " , causing " \
<< this->damage << " points of damage!" << std::endl;
}
void ClapTrap::takeDamage(unsigned int amount) {
if (this->energy <= amount) {
std::cout << this->Name << " is dead" << std::endl;
this->energy = 0;
}
else {
this->energy -= amount;
std::cout << "ClapTrap " << this->Name << \
" take Damage " << amount << " , remaning hp is " \
<< this->energy << std::endl;
}
}
void ClapTrap::beRepaired(unsigned int amount) {
if (this->energy == 0) {
std::cout << "ClapTrap " << this->Name << " is repaired by " << amount << " and comes back to life" << std::endl;
this->energy += amount;
}
else {
this->energy += amount;
std::cout << "ClapTrap " << this->Name << " is repaired by " << amount << " and remaning hp is " << this->energy << std::endl;
}
}
ClapTrap& ClapTrap::operator=( ClapTrap const& clap) {
this->Name = clap.Name;
this->Hitpoints = clap.Hitpoints;
this->energy = clap.energy;
this->damage = clap.damage;
std::cout << "ClapTrap "<< this->Name << " operator= is called" << std::endl;
return *this;
}
class ClapTrap {
private:
std::string Name;
unsigned int Hitpoints;
unsigned int energy;
unsigned int damage;
public:
void attack(std::string const & target);
void takeDamage(unsigned int amount);
void beRepaired(unsigned int amount);
ClapTrap& operator=( ClapTrap const& clap);
ClapTrap(std::string name);
ClapTrap();
~ClapTrap();
};
ScavTrap 클래스
void guarGate()
게이트 키퍼 모드에 진입했음을 표준출력으로 알린다.
#include "ClapTrap.hpp"
// ClapTrap을 상속받는다. public으로 받았으니 public보다
// 넓은 범위에 있는 것들은(없지만) 전부 public이 된다.
// (public보다 좁은 범위인 protected, pravte같은 것들은 그대로 상속받는다.)
class ScavTrap: public ClapTrap {
public:
ScavTrap();
ScavTrap( std::string _name);
~ScavTrap();
void attack(std::string const & target);
void takeDamage(unsigned int amount);
void beRepaired(unsigned int amount);
void guardGate();
ScavTrap& operator=( ScavTrap const& scav);
};
생성자의 실행순서는
부모 -> 자손
소멸 순서는
자손 -> 부모
그러므로 예제에서의 순서는
ClapTrap 생성자 -> ScavTrap 생성자 -> ScavTrap 소멸자 -> ClapTrap 소멸자
#include "ScavTrap.hpp"
// ClapTrap의 생성자를 실행시킨다.
ScavTrap::ScavTrap(): ClapTrap() {
this->Hitpoints = 100;
this->energy = 50;
this->damage = 20;
std::cout << "ScavTrap_Name : " << this->Name << std::endl;
}
ScavTrap::ScavTrap( std::string _name): ClapTrap(_name) {
this->Hitpoints = 100;
this->energy = 50;
this->damage = 20;
std::cout << "ScavTrap_Name : " << this->Name << std::endl;
}
ScavTrap::~ScavTrap() {
std::cout << "ScavTrap "<< this->Name << " has been destroyed." << std::endl;
}
ScavTrap& ScavTrap::operator=( ScavTrap const& scav) {
ClapTrap::operator=(scav);
std::cout << "ScavTrap " << this->Name << " operator= is called" << std::endl;
return *this;
}
void ScavTrap::attack(std::string const & target) {
std::cout << "ScavTrap " << this->Name << \
" attacks " << target << " , causing " \
<< this->damage << " points of damage!" << std::endl;
}
void ScavTrap::takeDamage(unsigned int amount) {
if (this->energy <= amount) {
std::cout << this->Name << " is dead" << std::endl;
this->energy = 0;
}
else {
this->energy -= amount;
std::cout << "ScavTrap " << this->Name << \
" take Damage " << amount << " , remaning hp is " \
<< this->energy << std::endl;
}
}
void ScavTrap::beRepaired(unsigned int amount) {
if (this->energy == 0) {
std::cout << "ScavTrap " << this->Name << " is repaired by " << amount << " and comes back to life" << std::endl;
this->energy += amount;
}
else {
this->energy += amount;
std::cout << "ScavTrap " << this->Name << " is repaired by " << amount << " and remaning hp is " << this->energy << std::endl;
}
}
void ScavTrap::guardGate() {
if (energy)
std::cout << "ScavTrap " << this->Name << " has entered gate guard mode." << std::endl;
else
std::cout << "ScavTrap " << this->Name << " cannot enter gate guard mode because " << this->Name << " is dead." << std::endl;
}
FragTrap
void highFivesGuys(void)
긍정적인 하이파이브 요청을 표준출력으로 표현
class FragTrap : public ClapTrap {
public:
FragTrap();
FragTrap( std::string name);
~FragTrap();
FragTrap& operator=( FragTrap const& frag);
void highFivesGuys(void);
};
#include "FragTrap.hpp"
FragTrap::FragTrap(): ClapTrap() {
this->Hitpoints = 100;
this->energy = 100;
this->damage = 30;
std::cout << "FragTrap_Name : " << this->Name << std::endl;
}
FragTrap::FragTrap( std::string name): ClapTrap(name) {
this->Hitpoints = 100;
this->energy = 100;
this->damage = 30;
std::cout << "FragTrap_Name : " << this->Name << std::endl;
}
FragTrap::~FragTrap() {
std::cout << "FragTrap " << this->Name << " has been destroyed." << std::endl;
}
FragTrap& FragTrap::operator=( FragTrap const& frag) {
ClapTrap::operator=(frag);
std::cout << "FragTrap " << this->Name << " operator= is called" << std::endl;
return *this;
}
void FragTrap::highFivesGuys(void) {
if(this->energy)
std::cout << "FragTrap " << this->Name << " asks for a positive high-five" << std::endl;
else
std::cout << "FragTrap " << this->Name << " can't ask for a positive high-five because " << this->Name << " is dead" << std::endl;
}
DiamondTrap 클래스
void whoAmI()
DiamondTrap 생성자의 이름과 ClapTrap 생성자의 이름을 호출하여 둘이 다른 이름이 초기화됐음을 보여준다.
현재 ScavTrap
클래스와 FragTrap
클래스는 ClapTrap
을 상속하고 있는 상태일 것이다. 여기서 DiamondTrap
클래스가 두개의 클래스를 상속한다면 위 그림처럼 다이아몬드 형태의 다중상속을 한다고 해서 다이아몬드 상속
이라고 불린다.
위의 경우 문제가 하나 발생하는데 저런 식으로 상속을 할 경우,
컴파일러는 ScavTrap의 상속자인 ClapTrap과 FragTrap의 상속자인 ClapTrap 두개의 ClapTrap을 컴파일하게 된다.
그래서 ClapTrap생성자를 호출할때 어떤 ClapTrap인지 알 수 없게 되어 오류가 발생한다.(발생안할 수도 있긴한다. 그때는 생성자가 두번 호출된다.)
해결책
각 클래스를 선언할때
class ScavTrap: public virtual ClapTrap
class FragTrap: public virtual ClapTrap
위처럼 선언하면 된다. Virtual은 가상 상속이라고 불리는 것으로, 위의 경우 이전에는 ClapTrap 클래스를 상속받아 새로운 클래스를 생성했다면 Virtual을 붙인 후에는 ClapTrap 클래스가 있는 곳을 가르킨다. 라고 생각하면 된다. 그렇게 되면 중복되지 않기 때문에 겹칠 일이 없게 된다.
// 아래처럼 코드를 짜면 FragTrap -> ScavTrap 순서로 진행된다.
class DiamondTrap: public FragTrap, public ScavTrap {
private:
std::string Name;
public:
DiamondTrap();
DiamondTrap( std::string n);
~DiamondTrap();
void attack(std::string const & target);
DiamondTrap& operator=( DiamondTrap const& Diamond);
void whoAmI();
};
#include "DiamondTrap.hpp"
// ClapTrap -> FrapTrap -> ScavTrap 순서로 생성자 호출
// 소멸은 반대로 된다.
DiamondTrap::DiamondTrap(): ClapTrap(), FragTrap(), ScavTrap(), Name(ClapTrap::Name) {
// 이 경우 마지막에 ScavTrap 생성자 값으로 세팅되기 때문에 FragTrap 값을 설정하려는
// 속성은 수동으로 바꿔주는 모습을 확인할 수 있다.
ClapTrap::Name += "_clap_name";
ClapTrap::damage = 30;
std::cout << "DiamondTrap_Name : " << this->Name << std::endl;
}
DiamondTrap::DiamondTrap(std::string n): ClapTrap(n), FragTrap(n), ScavTrap(n), Name(n) {
ClapTrap::Name += "_clap_name";
ClapTrap::damage = 30;
std::cout << "DiamondTrap_Name : " << this->Name << std::endl;
}
DiamondTrap::~DiamondTrap() {
std::cout << "DiamondTrap " << this->Name << " has been destroyed." << std::endl;
}
void DiamondTrap::attack(std::string const & target) {
ScavTrap::attack(target);
}
DiamondTrap& DiamondTrap::operator=( DiamondTrap const& Diamond) {
ClapTrap::operator=(Diamond);
std::cout << "DiamondTrap " << this->Name << " operator= is called" << std::endl;
return *this;
}
void DiamondTrap::whoAmI() {
std::cout << "DiamondTrap name is " << this->Name << " and ClapTrap name is " << ClapTrap::Name << std::endl;
}
#include <iostream>
int main()
{
int a;
a = 3;
{
int a;
a = 5;
std::cout << a << std::endl;
}
}
-Wshadow 옵션은 지역변수가 덮어씌워지거나 매개변수가 덮어씌워지면 경고를 해주는 옵션이다.
clang++ -Wshadow test.cpp 로 컴파일을 할 경우 경고를 해준다.
그리고 여기서 clang++ -Wshadow -Wno-shadow test.cpp 로 컴파일을 할 경우 두번째 사진처럼 경고가 없어진다.
-Wno-shadow는 -Wshadow의 경고가 안나오게 해주는 옵션이다.