CPP-Module-01

MELIES·2025년 2월 24일

ex00

implement the two following functions:
• Zombie* newZombie( std::string name );
It creates a zombie, name it, and return it so you can use it outside of the function
scope.
• void randomChump( std::string name );
It creates a zombie, name it, and the zombie announces itself

Zombie class

class Zombie {
private:
    std::string _name;
public:
    Zombie(std::string name);
    ~Zombie();
//  생성/소멸 시 디버깅 메시지 출력
    void announce();
};

메모리 할당

동적 할당 (힙 메모리)

Zombie* newZombie(std::string name) {
    Zombie* ptr = new Zombie(name);
    return ptr;
}
  • new 연산자로 동적 할당
  • 단 수동으로 메모리 해제 필요!
  • 함수 스코프 밖에서도 사용 가능

스택 메모리 할당 (자동 할당)

void randomChump(std::string name) {
    Zombie myZombie(name);
    myZombie.announce();
}   
  • 함수 종료 시 자동으로 소멸
  • 메모리 해제 필요 없음
  • 함수 스코프 내에서만 사용 가능

Test

int main() {
    Zombie myZombie("stack1");        // 스택 할당
    randomChump("stack2");            // 스택 할당 후 자동 소멸
    Zombie* heap = newZombie("heap"); // 힙 할당
    heap->announce();
    delete heap;                      // 수동 메모리 해제
    return 0;
}
stack1 is created              // 1️⃣ 스택 좀비 생성
stack2 is created              // 2️⃣ randomChump 안에서 좀비 생성
stack2: BraiiiiiiinnnzzzZ...   // 2️⃣ randomChump 안에서 announce 호출
stack2 is dead                 // 2️⃣ randomChump 함수 종료로 소멸자 호출
heap is created                // 3️⃣ 힙 좀비 생성
heap: BraiiiiiiinnnzzzZ...     // 4️⃣ announce 호출
heap is dead                   // 5️⃣ delete로 인한 소멸자 호출
stack1 is dead                 // 6️⃣ main 함수 종료로 스택 좀비 소멸


스택 할당

객체의 수명이 특정 스코프로 제한될 때
간단하고 빠른 메모리 할당이 필요할 때
자동 메모리 관리가 필요할 때

힙 할당

객체가 함수 범위를 벗어나서 존재해야 할 때
런타임에 메모리 크기가 결정될 때
큰 크기의 객체를 다룰 때

ex01


Implement the following function in the appropriate file:
Zombie* zombieHorde( int N, std::string name );

Zombie class

class Zombie {
private:
    std::string _name;
public:
    Zombie();                         
    ~Zombie();
    void setName(std::string name);
    void announce();
};

Zombie Horde 구현

Zombie* zombieHorde(int N, std::string name) {
    Zombie* zombies = new Zombie[N];  // N개의 좀비 배열 생성
    
    for (int i = 0; i < N; i++) {
        zombies[i].setName(name);     
    }
    return zombies;
}

new Zombie[N]을 사용해 배열 형태로 동적 할당

Test

int main() {
    Zombie* zombies = zombieHorde(5, "Foo");
    for (int i = 0; i < 5; i++) {
        zombies[i].announce();
    }
    delete[] zombies;
    return 0;
}

delete[]를 사용해 메모리 해제

Foo: BraiiiiiiinnnzzzZ...  // 첫 번째 좀비
Foo: BraiiiiiiinnnzzzZ...  // 두 번째 좀비
Foo: BraiiiiiiinnnzzzZ...  // 세 번째 좀비
Foo: BraiiiiiiinnnzzzZ...  // 네 번째 좀비
Foo: BraiiiiiiinnnzzzZ...  // 다섯 번째 좀비
Foo is dead                // 소멸자 호출 (5번)
Foo is dead
Foo is dead
Foo is dead
Foo is dead

정리

배열 소멸 시 각 객체의 소멸자 호출 보장

동적할당 시 소멸자

// 단일 객체
Zombie* heap = new Zombie("heap");
delete heap;        // delete 시 소멸자 호출

// 객체 배열
Zombie* zombies = new Zombie[5];
delete[] zombies;   // 배열의 각 객체마다 소멸자 호출

스택 할당 시 소멸자

void foo() {
    Zombie stack("stack");  // 생성자 호출
}                          // 함수 종료 시 자동으로 소멸자 호출

int main() {
    Zombie z("main");      // 생성자 호출
    foo();
    return 0;              // main 함수 종료 시 z의 소멸자 호출
}

스코프를 벗어날 때 자동으로 소멸자 호출
별도의 delete 필요 없음
프로그램 종료 시 남아있는 스택 객체들의 소멸자가 호출됨

int main() {
    Zombie a("A");             // 스택 객체 생성
    Zombie* b = new Zombie("B"); // 힙 객체 생성
    {
        Zombie c("C");         // 새로운 스코프의 스택 객체
    }                          // C의 소멸자 호출
    delete b;                  // B의 소멀자 호출
    return 0;                  // A의 소멸자 호출
}

ex02

Write a program that contains
• A string variable initialized to "HI THIS IS BRAIN".
• stringPTR: A pointer to the string.
• stringREF: A reference to the string.
Your program has to print
• The memory address of the string variable.
• The memory address held by stringPTR.
• The memory address held by stringREF.
And then
• The value of the string variable.
• The value pointed to by stringPTR.
• The value pointed to by stringREF.

int main()
{
    std::string str = "HI THIS IS BRAIN";
    std::string *stringPTR = &str;        // 포인터
    std::string &stringREF = str;         // 참조자

    // 메모리 주소 출력
    std::cout << "문자열 변수의 메모리 주소 : " << &str << std::endl;
    std::cout << "stringPTR이 가리키는 메모리주소 : " << stringPTR << std::endl;
    std::cout << "stringREF가 참조하는 메모리주소 : " << &stringREF << std::endl;

    // 값 출력
    std::cout << "문자열 변수의 값 : " << str << std::endl;
    std::cout << "stringPTR이 가리키는 값 : " << *stringPTR << std::endl;
    std::cout << "stringREF가 참조하는 값 : " << stringREF << std::endl;
}
문자열 변수의 메모리 주소 : 0x7fff5fbff6e0
stringPTR이 가리키는 메모리주소 : 0x7fff5fbff6e0
stringREF가 참조하는 메모리주소 : 0x7fff5fbff6e0

문자열 변수의 값 : HI THIS IS BRAIN
stringPTR이 가리키는 값 : HI THIS IS BRAIN
stringREF가 참조하는 값 : HI THIS IS BRAIN

포인터(Pointer)

  • 메모리 주소를 저장하는 변수
  • 애스테리스크 연산자로 선언
  • & 연산자로 주소 값을 가져옴
  • 애스테리스크 연산자로 역참조하여 값에 접근

참조자(Reference)

  • 변수의 별칭
  • & 연산자로 선언
  • 선언과 동시에 초기화 필요 *
  • 원본 변수와 동일한 메모리 주소 공유

포인터, 참조자 비교

포인터참조자
초기화나중에 초기화 가능선언과 동시에 초기화 필요
재할당다른 주소로 변경 가능한번 참조하면 변경 불가
nullnull 가능null 불가능

ex03

Implement a Weapon class that has
• A private attribute type, which is a string.
• A getType() member function that returns a const reference to type.
• A setType() member function that sets type using the new one passed as parameter.

create two classes
HumanA and HumanB. They both have a Weapon and a
name. They also have a member function attack() that displays (of course, without the angle brackets):

<name> attacks with their <weapon type>

HumanA and HumanB are almost the same except for these two tiny details
• While HumanA takes the Weapon in its constructor, HumanB doesn’t.
• HumanB may not always have a Weapon, whereas HumanA will always be armed.

Weapon

class Weapon {
private:
    std::string _type;
public:
    const std::string &getType();  // 참조자 반환
    void setType(std::string str);
    Weapon(std::string type);
};

HumanA, HumanB

class HumanA {
private:
    std::string _name;
    Weapon &_weapon;  // 참조자로 무기 소유
public:
    HumanA(std::string name, Weapon &humanWeapon);
    void attack();
};
class HumanB {
private:
    std::string _name;
    Weapon *_weapon;  // 포인터로 무기 소유
public:
    HumanB(std::string name);
    void setWeapon(Weapon &weapon);
    void attack();
};

초기 세팅 비교

// 참조자 사용 (항상 무기를 가짐)
HumanA::HumanA(std::string name, Weapon &humanWeapon) 
    : _name(name), _weapon(humanWeapon) {}  // 초기화 필수

// 포인터 사용 (무기를 가질 수도, 안 가질 수도 있음)
HumanB::setWeapon(Weapon &weapon) {
    _weapon = &weapon;  // 나중에 무기 설정 가능
}

HumanB::HumanB(std::string name) : _name(name) {}

Test

int main() {
    {
        Weapon club = Weapon("crude spiked club");
        HumanA bob("Bob", club);
        bob.attack();  // "Bob attacks with their crude spiked club"
        club.setType("some other type of club");
        bob.attack();  // "Bob attacks with their some other type of club"
    }
    {
        Weapon club = Weapon("crude spiked club");
        HumanB jim("Jim");
        jim.setWeapon(club);
        jim.attack();  // "Jim attacks with their crude spiked club"
        club.setType("some other type of club");
        jim.attack();  // "Jim attacks with their some other type of club"
    }
}
  1. 참조자(Reference)의 특징

선언과 동시에 초기화 필요
null이 될 수 없음
한번 참조하면 다른 객체 참조 불가
HumanA는 항상 무기를 가져야 할 때 적합

  1. 포인터(Pointer)의 특징

나중에 초기화 가능
null이 될 수 있음
다른 객체를 가리키도록 변경 가능
HumanB는 무기를 선택적으로 가질 수 있을 때 적합

참조자: 항상 유효한 객체가 필요한 경우
포인터: 선택적이거나 변경 가능한 관계에 적합

ex05

create a Harl class with the following private member functions:
• void debug( void );
• void info( void );
• void warning( void );
• void error( void );
Harl also has a public member function that calls the four member functions above
depending on the level passed as parameter:
void complain( std::string level );

Harl has to complain without using a forest of if/else if/else. It doesn’t think twice!

Harl

class Harl {
private:
    void debug();
    void info();
    void warning();
    void error();
public:
    void complain(std::string level);
};

함수 포인터 사용

void Harl::complain(std::string level) {
    // 함수 포인터 배열 선언
    void (Harl::*functions[4])(void) = {
        &Harl::debug,
        &Harl::info,
        &Harl::warning,
        &Harl::error
    };
    
    std::string levels[4] = {"DEBUG", "INFO", "WARNING", "ERROR"};

    // 레벨에 맞게 함수 호출
    for (int i = 0; i < 4; i++) {
        if (level == levels[i]) {
            (this->*functions[i])();
            return;
        }
    }
}
  • 클래스 멤버 함수를 가리키는 포인터
  • 배열로 관리해서 동적으로 함수 호출 가능
  • this 포인터와 함께 사용 객체의 컨텍스트에서 함수 실행

Test

int main() {
    Harl harl;
    harl.complain("DEBUG");   // Debug 메시지 출력
    harl.complain("INFO");    // Info 메시지 출력
    harl.complain("WARNING"); // Warning 메시지 출력
    harl.complain("ERROR");   // Error 메시지 출력
    return 0;
}

ex06

ex05에서 if, else if의 제한이 있었다면 여기선 switch

Switch문 사용

void Harl::complain(std::string level) {
    void (Harl::*functions[4])(void) = {
        &Harl::debug,
        &Harl::info,
        &Harl::warning,
        &Harl::error
    };
    std::string levels[4] = {"DEBUG", "INFO", "WARNING", "ERROR"};
    
    int n;
    for (n = 0; n < 4; n++) {
        if (level == levels[n])
            break;
    }
    
    switch (n) {
    case 0:
        (this->*functions[0])();  // DEBUG
    case 1:
        (this->*functions[1])();  // INFO
    case 2:
        (this->*functions[2])();  // WARNING
    case 3:
        (this->*functions[3])();  // ERROR
        break;
    default:
        std::cout << "[ Probably complaining about insignificant problems ]" << std::endl;
        break;
    }
}
  • break가 없는 구조로 cascade 효과 구현
  • 선택된 레벨부터 ERROR까지 모든 메시지 출력
  • 존재하지 않는 레벨에 대한 기본 메시지 처리

간단한 인자 처리

int main(int ac, char **av) {
    Harl harl;
    
    if (ac != 2) {
        std::cout << "Wrong Type" << std::endl;
        exit(EXIT_FAILURE);
    }
    harl.complain(av[1]);
    return 0;
}

출력

./harl DEBUG
[ DEBUG ]
I love having extra bacon for my 7XL-double-cheese-triple-pickle-specialketchup burger.
I really do!

.
.
.

[ ERROR ]
This is unacceptable! I want to speak to the manager now.

./harl INFO
[ INFO ]
I cannot believe adding extra bacon costs more money.
You didn't put enough bacon in my burger!
If you did, I wouldn't be asking for more!

[ WARNING ]
I think I deserve to have some extra bacon for free.
I've been coming for years whereas you started working here since last month.

[ ERROR ]
This is unacceptable! I want to speak to the manager now.

./harl WARINING	
...

./harl ERROR
[ ERROR ]
This is unacceptable! I want to speak to the manager now.

./harl something
[ Probably complaining about insignificant problems ]

./harl
Wrong Type
profile
42 Seoul

0개의 댓글