[C++] const, static

muchan
muchan
∙∙조회 6

https://modoocode.com/197

위 링크에서 공부한 내용 정리입니다.


생성자의 초기화 리스트

scss
class Marine {
  int hp;                // 마린 체력
  int coord_x, coord_y;  // 마린 위치
  int damage;            // 공격력
  bool is_dead;

 public:
  Marine();              // 기본 생성자
  Marine(int x, int y);  // x, y 좌표에 마린 생성

  int attack();                       // 데미지를 리턴한다.
  void be_attacked(int damage_earn);  // 입는 데미지
  void move(int x, int y);            // 새로운 위치

  void show_status();  // 상태를 보여준다.
};

Marine::Marine() : hp(50), coord_x(0), coord_y(0), damage(5), is_dead(false) {}

Marine::Marine(int x, int y)
    : coord_x(x), coord_y(y), hp(50), damage(5), is_dead(false) {}

위 코드에서

css
Marine::Marine() : hp(50), coord_x(0), coord_y(0), damage(5), is_dead(false) {}

Marine::Marine(int x, int y)
    : coord_x(x), coord_y(y), hp(50), damage(5), is_dead(false) {}

해당 코드들을 생성자 초기화 리스트라고 한다

css
(생성자 이름) : var1(arg1), var2(arg2) {}

일반적인 꼴은 위와 같다

디폴트 생성자와 생성자 초기화 리스트의 차이

  • 생성자 초기화 리스트: 생성과 초기화를 동시에
  • 디폴트 생성자: 생성을 먼저하고 그 다음에 대입
ini
// 생성자 초기화 리스트
int a = 10;

// 디폴트 생성자
int a;
a = 10;

이런 느낌이라고 한다

생성과 동시에 초기화 되어야 하는 것

  • 상수
  • 레퍼런스

상수와 레퍼런스는 모두 생성과 동시에 초기화되어야 하며, 그렇지 않으면 컴파일 오류가 난다

arduino
#include <iostream>

class Marine {
  int hp;                // 마린 체력
  int coord_x, coord_y;  // 마린 위치
  bool is_dead;

  const int default_damage;  // 기본 공격력

 public:
  Marine();              // 기본 생성자
  Marine(int x, int y);  // x, y 좌표에 마린 생성

  int attack();                       // 데미지를 리턴한다.
  void be_attacked(int damage_earn);  // 입는 데미지
  void move(int x, int y);            // 새로운 위치

  void show_status();  // 상태를 보여준다.
};
Marine::Marine()
    : hp(50), coord_x(0), coord_y(0), default_damage(5), is_dead(false) {}

Marine::Marine(int x, int y)
    : coord_x(x), coord_y(y), hp(50), default_damage(5), is_dead(false) {}

default_damage가 const여서 생성과 동시에 초기화해주는 모습이다

const는 한번 초기화 되면 값을 바꿀 수 없다

css
Marine::Marine(int x, int y, int default_damage)
    : coord_x(x),
      coord_y(y),
      hp(50),
      default_damage(default_damage),
      is_dead(false) {}

이런식으로 생성할 때 값을 전달 받아서 초기화 할 수도 있다

ini
const int default_damage = (인자로 받은 default_damage);

위에 코드는 이걸 실행하는 것과 똑같다고 한다

static 변수

전역 변수 같지만 클래스 하나에만 종속되는 변수

scss
// static 멤버 변수의 사용

#include <iostream>

class Marine {
  static int total_marine_num;

  int hp;                // 마린 체력
  int coord_x, coord_y;  // 마린 위치
  bool is_dead;

  const int default_damage;  // 기본 공격력

 public:
  Marine();              // 기본 생성자
  Marine(int x, int y);  // x, y 좌표에 마린 생성
  Marine(int x, int y, int default_damage);

  int attack();                       // 데미지를 리턴한다.
  void be_attacked(int damage_earn);  // 입는 데미지
  void move(int x, int y);            // 새로운 위치

  void show_status();  // 상태를 보여준다.

  ~Marine() { total_marine_num--; }
};
int Marine::total_marine_num = 0;

Marine::Marine()
    : hp(50), coord_x(0), coord_y(0), default_damage(5), is_dead(false) {
  total_marine_num++;
}

Marine::Marine(int x, int y)
    : coord_x(x), coord_y(y), hp(50), default_damage(5), is_dead(false) {
  total_marine_num++;
}

Marine::Marine(int x, int y, int default_damage)
    : coord_x(x),
      coord_y(y),
      hp(50),
      default_damage(default_damage),
      is_dead(false) {
  total_marine_num++;
}
void create_marine() {
  Marine marine3(10, 10, 4);
  marine3.show_status();
}
int main() {
  Marine marine1(2, 3, 5);
  marine1.show_status();

  Marine marine2(3, 5, 10);
  marine2.show_status();

  create_marine();

  std::cout << std::endl << "마린 1 이 마린 2 를 공격! " << std::endl;
  marine2.be_attacked(marine1.attack());

  marine1.show_status();
  marine2.show_status();
}

여기서 create_marine()에서 marine3이 생성되고 함수가 끝날 때 소멸되어
static 변수인 total_marine_num이 증가했다가 감소한다

초기화

모든 전역 및 static 변수들은 정의와 동시에 값이 자동으로 0으로 초기화된다
따로 초기화 하지 않아도 되지만 클래스 static변수들은 아래와 같이 초기화한다

ini
int Marine::total_marine_num = 0;

아래처럼은 초기화 시키지 못한다

perl
class Marine {
  static int total_marine_num = 0;
}

이게 가능한 꼴은 const static 변수일 때만 가능하다

perl
class Marine {
  const static int x = 0;
}

호출 방식

static 변수는 객체 없이도 호출할 수 있어 (객체).(멤버 함수)가 아니라 (클래스)::(static 함수) 형식으로 호출한다

중요!) static 함수 내에서는 클래스의 static 변수만을 이용해야한다

this

ini
Marine& Marine::be_attacked(int damage_earn) {
  hp -= damage_earn;
  if (hp <= 0) is_dead = true;

  return *this;
}

여기서 this는 객체 자신을 가리키는 포인터다

레퍼런스를 리턴하는 함수

ini
// 레퍼런스를 리턴하는 함수
#include <iostream>

class A {
  int x;

 public:
  A(int c) : x(c) {}

  int& access_x() { return x; }
  int get_x() { return x; }
  void show_x() { std::cout << x << std::endl; }
};

int main() {
  A a(5);
  a.show_x();

  int& c = a.access_x();
  c = 4;
  a.show_x();

  int d = a.access_x();
  d = 3;
  a.show_x();

  // 아래는 오류
  // int& e = a.get_x();
  // e = 2;
  // a.show_x();

  int f = a.get_x();
  f = 1;
  a.show_x();
}
ini
int& c = a.access_x();
c = 4;
a.show_x();

여기서 c는 x의 레퍼런스(x의 별명)을 받는다

ini
int &c = x; 

이거랑 같은 구조다

ini
int d = a.access_x();
d = 3;
a.show_x();

이건 d가 그냥 int 변수이므로 값 복사가 일어나 d에는 x의 값이 들어간다
(컴파일 에러가 난다)

이런 구조라고 한다

예전에는 이 내용을 이해하지 못했는데 지금 이해가 되는 걸 보니 C# 공부를 나쁘지 않게 한 것 같다

kotlin
Marine& Marine::be_attacked(int damage_earn) {
  this->hp -= damage_earn;
  if (this->hp <= 0) this->is_dead = true;

  return *this;
}

비슷한 예제로 위의 함수가 있다면

less
marine2.be_attacked(marine1.attack()).be_attacked(marine1.attack());

이런식으로 동작한다

하지만 Marine&을 Marine으로 바꾸면 값 복사로 동작해서 한번밖에 실행되지 않는다

const 함수

csharp
int attack() const; 

C++에서는 변수들의 값을 바꾸지 않고 읽기만 하는 상수같은 멤버 함수를 상수 함수로 선언할 수 있다

arduino
(기존의 함수의 정의) const;

이런식으로 선언한다고 한다