[C++] 4.3 클래스 - const, static
생성자 초기화 리스트(initializer list)
전에 하던 그 생성자가 아니라, C++스러운 생성자를 만들어보자.
#include <iostream>
class Marine{
int hp;
int coord_x, coord_y;
int damage;
bool is_dead;
public :
Marine();
Marine(int x, int 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){}
void Marine::move(int x, int y) {
coord_x = x;
coord_y = y;
}
int Marine::attack() { return damage; }
void Marine::be_attacked(int damage_earn) {
hp -= damage_earn;
if (hp <= 0) is_dead = true;
}
void Marine::show_status() {
std::cout << " *** Marine *** " << std::endl;
std::cout << " Location : ( " << coord_x << " , " << coord_y << " ) "
<< std::endl;
std::cout << " HP : " << hp << std::endl;
}
int main() {
Marine marine1(2, 3);
Marine marine2(3, 5);
marine1.show_status();
marine2.show_status();
}
익숙하지 않은 생성자 친구가 눈에 보인다.
Marine::Marine() :
hp(50), coord_x(0), coord_y(0),
damage(5), is_dead(false){}
- 함수 {본체} 에는 아무것도 없고, 앞에 이상한게 덕지덕지 붙어있다.
Marine::Marine() {
hp = 50;
coord_x = coord_y = 0;
damage = 5;
is_dead = false;
}
- 원래는 이렇게 생긴 친구다.
생성자 리스트
: hp(50), coord_x(0), coord_y(0),
damage(5), is_dead(false){}
생성자 이름 : var1(args), var2(args){}
- 이렇게 생성자에서 멤버변수 초기화를 하는 이 친구가 생성자 리스트이다.
- 리스트 초기화가 좋은 이유는
- 변수를 초기화 할 때 다이렉트로 한번에 초기화 하기 때문에, 효율적이고 확실하다.
- 다만 멤버변수 전부다 함께 초기화 시켜줘야함.
static
static 멤버 변수
C에서 쓰던 그 static 맞다.
클래스에서도 사용가능하고, C랑 똑같이 공유가 되고, main문 종료전까지 계속 살아있다.
class Marine{
static int total_marine_num;
int hp;
int coord_x, coord_y;
int damage;
bool is_dead;
const int default_damage;
public :
Marine();
Marine(int x, int y);
Marine::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_num = 0;
Marine::Marine() :
hp(50), coord_x(0), coord_y(0),
damage(5), is_dead(false), default_damage(5){total_marine_num++;}
...
- 해당 클래스의 static 멤버변수이다.
- 이 클래스를 사용하는 모든 클래스들이 공유하는 변수이다.
- static 멤버변수는 그 어느객체의 것도 아니기때문에, 이렇게 변수를 선언해도 된다.
int Marine::total_marine_num = 0;
이 static 멤버변수를 이렇게 사용한다.
Marine::Marine() :
hp(50), coord_x(0), coord_y(0),
damage(5), is_dead(false), default_damage(5){total_marine_num++;}
- 생성할 땐 이 static변수를 +1 시켰고
~Marine() {total_marine_num--;}
- 소멸될땐 -1 시켰다.
- 이런식으로 사용하면 된다.
static 멤버 함수
class Marine{
static int total_marine_num;
int hp;
int coord_x, coord_y;
int damage;
bool is_dead;
const int default_damage;
public :
Marine();
Marine(int x, int y);
Marine::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();
static void show_total_marine();
~Marine() {total_marine_num--;}
};
...
void Marine::show_total_marine() {
std::cout << "전체 마린 수 : " << total_marine_num << std::endl;
}
...
// main()
Marine::show_total_marine();
- static 멤버변수를 선언하였다.
- static 함수는 어떤 객체에 종속되는 것이 아니라, 클래스 자체에 종속된다
- (객체).(멤버 함수) 이렇게 부르는게 아니라
클래스::static 함수() 이렇게 불러야한다.
- (객체).(멤버 함수) 이렇게 부르는게 아니라
this
this 는 객체 자신을 가리키는 키워드이다.
class Marine{
static int total_marine_num;
const static int i =0;
int hp;
int coord_x, coord_y;
int damage;
bool is_dead;
const int default_damage;
public :
Marine();
Marine(int x, int y);
Marine::Marine(int x, int y, int default_damage);
int attack();
Marine& be_attacked(int damge_earn);
void move(int x, int y);
void show_status();
static void show_total_marine();
~Marine() {total_marine_num--;}
};
...
Marine& Marine::be_attacked(int damage_earn) {
hp -= damage_earn;
if (hp <= 0) is_dead = true;
return *this;
}
...
// main()
marine2.be_attacked(marine1.attack()).be_attacked(marine1.attack());
이런식으로 쓰인다.
Marine& Marine::be_attacked(int damage_earn) {
hp -= damage_earn;
if (hp <= 0) is_dead = true;
return *this;
}
이친구의 변수 hp를 this->hp라고 변경해도, 위의 함수와 의미는 같다.
Marine& Marine::be_attacked(int damage_earn) {
this->hp -= damage_earn;
if (hp <= 0) is_dead = true;
return *this;
}
- 객체 내에서 내꺼다! 표시하는게 this 라는 표현이며, 멤버변수, 멤버함수 둘다 적용가능하다.
- 당연히 클래스꺼인 static 함수,변수는 객체꺼 표시인 this를 표시 못한다.
근데 함수 리턴값이 Marine& 인데, 이 레퍼런스(별명, alias)를 리턴한다는 게 무슨뜻일까?
레퍼런스를 리턴하는 함수
#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();
}
- 결과
여기서 int를 리턴하는 'get_x()' 함수와, int의 레퍼런스를 리턴하는 'access_x()'함수가 사용됨.
아으 헷갈려.. 어케 돌아가는지 차근차근 보자
int& c = a.access_x();
c = 4;
a.show_x();
- c는 지금 x의 레퍼런스를 받았다. 즉 c는 x의 별명이 된것.
- c를 4로 바꿨으니, x값도 변경되었다.
레퍼런스를 리턴하는 함수는 그 함수 부분을 원래의 그 멤버변수로 치환했다고 생각해도
상관없음.int& c = x; // int x의 새 별명은 c
그렇담 int 형에 레퍼런스를 넣었는데 컴파일 에러가 안나오는 걸까
int d = a.access_x();
d=3;
a.show_x();
- int d 에 그냥 x의 레퍼런스 관련 값이 들어갔음.
- d는 그냥 int 형 변수로써 값을 받아온거 이기 때문에, 컴파일 에러가 안나옴.
- 그렇기 때문에,
d=3;
을 넣었을 때, 객체에 x값이 변하지 않는게 당연하다.
에러가 나온 코드를 확인해보자.
int& e = a.get_x();
e = 2;
a.show_x();
- x의 값을 리턴받은 레퍼런스변수 e는 아무짝에 쓸모없는 친구가 되어버렸다.
- 그래서 에러가 난다. (애초에 타입도 안맞고, 사라지는 값을 별명으로 붙이는거도 말도안됨)
int& e = x(지역변수로 복사된 x);
마지막으로 확인해볼 친구는 int형에 객체에서 int형 값을 받아온 경우를 보자.
int f = a.get_x();
f = 1;
a.show_x();
- f에 x값을 받아오는 것을 확인 할 수 있다.
- 하지만 x의 '값'을 받아온거지 x를 받아온 것이 아니기 때문에, 당연히
f=1
을 해도 값이 객체의 맴버값이 바뀌진 않는다.
이제 다시 Marine예제로 가보자.
Marine& Marine::be_attacked(int damage_earn) {
this->hp -= damage_earn;
if (this->hp <= 0) this->is_dead = true;
return *this;
}
- be_attacked에서 &Marine 타입으로
*this
를 리턴받았다. - 여태 자신의 멤버변수를 리턴할 때
(*this).멤버변수
를this->멤버변수
처럼 써준 것이었다. *this
는 객체 자신을 의미한다.
그렇담 이 부분에서 어떤일이 벌어질까
marine2.be_attacked(marine1.attack()).be_attacked(marine1.attack());
- marine2.be_attacked(marine1.attack()) 이 실행되고, .be_attacked(marine1.attack()) 이 실행되는 것으로 확인할 수 있다.
- marine2.be_attacked(marine1.attack())이 실행될 때,
marine2에서 marine1의 공격을 받아낸 후, Marine&타입으로 marine2의 자신을 리턴 받고,
다시 marine2.be_attacked(marine1.attack()) 으로 공격을 또 받아낸 후 자신을 리턴받는다.
- marine2.be_attacked(marine1.attack())이 실행될 때,
Marine& 타입이 아니라, Marine 타입으로 *this
를 리턴한다면 어떻게 될까?
Marine Marine::be_attacked(int damage_earn) {
this->hp -= damage_earn;
if (this->hp <= 0) this->is_dead = true;
return *this;
}
- 컴파일도 되고 동작도 된다.
그렇담 이 부분에서 어떤일이 벌어질까
marine2.be_attacked(marine1.attack()).be_attacked(marine1.attack());
- 순서는 똑같이 두번 attack이 실행된다.
- 그런데 레퍼런스
&Marine의 *this
를 가져온게 아니라 그냥Marine의 *this
를 가져와서 동작한다- be_atttacked에서 임시객체의 Marine의
*this
를 리턴한다. - '임시객체 Marine을 계속 새로 불러서' attack을 하는 것이기 때문에, 암만 attack()을 해도 한번밖에 실행이 안되는 것을 확인할 수 있다.
- be_atttacked에서 임시객체의 Marine의
아니 그럼 위에 &Marine 의 *this
는 어떻게 두번 attack() 이 들어간거야?
- 레퍼런스(별명)로 리턴을 해버려서, 임시객체가 계속 새로 생성이 되는것이 아니라,
레퍼런스끼리 꼬리 물기를 해버려서 attack()이 이어지게 된다.
const 함수
- 변수의 값을 바꾸지 않고 읽기만하는 상수함수로써 선언 가능하다.
class Marine {
static int total_marine_num;
const static int i = 0;
int hp;
int coord_x, coord_y;
bool is_dead;
const int default_damage;
public:
Marine();
Marine(int x, int y);
Marine(int x, int y, int default_damage);
int attack() const; // <- const 함수
Marine& be_attacked(int damage_earn);
void move(int x, int y);
void show_status();
static void show_total_marine();
~Marine() { total_marine_num--; }
};
...
int Marine::attack() const { return default_damage; }
(기존 함수 정의) const;
이렇게 사용한다.
class Marine {
static int total_marine_num;
const static int i = 0;
int hp;
int coord_x, coord_y;
bool is_dead;
const int default_damage;
public:
Marine();
Marine(int x, int y);
Marine(int x, int y, int default_damage);
int attack() const; // <- const 함수
Marine& be_attacked(int damage_earn);
void move(int x, int y);
void show_status();
static void show_total_marine();
~Marine() { total_marine_num--; }
};
...
int Marine::attack() const { return default_damage; }
(기존 함수 정의) const;
이렇게 사용한다.따라하자 씹어먹는 C++
Author And Source
이 문제에 관하여([C++] 4.3 클래스 - const, static), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@psh4204/7.-C-4.3-const-static저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)