이번 과제 static 에 관한 조사이다.
해당 조사는 접은글의 자료를 참조 하였다.
static 멤버 변수
우선 static 멤버 변수에 대하여 알아 보도록 한다.
static 멤버 변수란? 모든 객체가 한 메모리를 공유 하는 멤버 변수를 의미하며,
객체마다 각각 할당되는 멤버가 아니라 모든 객체가 공유 가능한 멤버를 의미한다.
- static 멤버 변수는 모든 객체가 공유하는 멤버 변수 이므로
메모리는 프로그램 시작시부터 공간을 차지하고 있으며 메모리내에 값이 계속 유지된다.
-객체 생성 전에도 메모리가 존재 한다.
-객체와는 따로 독립적으로 존재 한다.
-때문에 객체 생선 전에 반드시 미리 초기화를 해주어야 한다.
-객체의 이름으로도 접근이 가능하며 클래스이름으로도 접근이 가능하다!
int Something::s_value = 1;
-위코드의 s_value 가 멤버 변수라면, 객체1의 s_value 그리고 객체2의 &s_value 의 주소는 동일하다.
(Why?? 모든 객체가 공유하는 멤버 변수이기 때문이다!)
-어떤 특정 타입의 모든 객체들의 수 라던지 모든 객체들이 알고 있고 공유할 수 있어야 하는 멤버 변수는
위와같은 특성을 이용하여 static 으로 사용한다.
static 멤버 변수의 선언과 정의
[ 선언과 정의의 구분 ]
- 선언
-static 멤버 변수의 선언은 컴파일러에게 변수의 정보만을 주며 실제로 메모리를 사용 하지는 않는다.
// 예시 코드는 static 멤버변수 m_value의 존재를 컴파일러에 정보를 주는것 뿐.
class Something
{
public:
static int m_value; //선언만! 메모리할당X
}
- 정의
-static 멤버 변수의 정의는 선언과달리 실제 메모리가 할당된다.
-초기화 하는 것도 정의해주는 과정이다! ( 초기화 == 값을넣는 작업이기때문! / 0으로초기화해도 0이들어가는것!)
// 정의! 전역 범위의 메모리를 할당 받는다. (!전역범위! 중요)
int Something::m_value = 1; // 1로 초기화(정의) 메모리에 1할당!
static 멤버 변수는 모든 객체가 공유해야 하므로 프로그램 전체 영역에서 메모리 유지가 되어야 한다.
따라서! 반드시! 지역범위가아닌! 전역범위에서 정의 및 초기화를 해주어야 한다.
- 전역 범위에서 [ 자료형이름 클래스이름 : : static변수이름 = 초기할값 ] 의 형식으로 초기화한다.
//코드 예시 1
class
{
public:
static in m_value = 1 // error !! 클래스 지역에서 초기화불가!!
}
// 코드 예시2
int Someting::m_value = 1; // 전역범위 정의 및 초기화 가능!
-위 코드예시1에서는 static 멤버 변수를 클래스 내에서 초기화 하면 객체를 생성할 때마다 static 멤버 변수 값이 계속 해서
덮어 씌워지게 된다. 왜냐하면 모든 객체들이 이 멤버를 같이 공유하게 되기 때문이다!
-static 멤버 변수는 클래스 내에서 선언만 가능하고 정의는 x
- static 멤버 변수는 모든 객체들이 접근해야 하므로 프로그램 시작부터 끝까지 유지되어야 하기떄문에
- 전역 범위 에서만 정의(초기화)할 수 있다!
-따라서 static 멤버 변수는 main 함수는 물론이고 생성자 안에서도 초기화 할 수 없다!
-생성자도 static 이어야 static 변수를 초기화 시킬 수 있는데 C++에서는 static 생성자를 지원하지 않는다.
#include<iostream>
using namespace std;
class Something
{
public:
static int m_value; // 선언
};
int Something::m_value = 1; // 전역범위에서 값을 1로 할당 및 초기화
int main()
{
// int Something::m_value = 1; // 전역이아닌 곳에서 할당 및 초기화 불가능!
cout << Something::m_value << endl; // 객체 생성전 사용가능 ! 1 이 출력됨 !
cout << &Something::m_value << endl; // 객체 생성전 사용가능 ! 주소값이 출력됨 !
Something st1; // st1 객체 생성
Something st2; // st2 객체 생성
st1.m_value = 2; // st1 객체의 m_value 값은 2로 대입!
cout << st1.m_value << endl; // 새로대입된 값 2가 출력됨!
cout << &st1.m_value << endl; // 새로운 객체이지만 m_value 의 주소와 같은주소출력!
st2.m_value = 222; // st2 객체의 m_value 값은 222로 대입!
cout << st2.m_value << endl; // 새로대입된 값 222가 출력됨!
cout << &st2.m_value << endl; // 새로운 객체이지만 m_value 의 주소와 같은주소출력!
return 0;
}
코드실행 결과는 아래 접은글 참고!
-실행 결과를 확인 하였으니 &st1.m_value, &st2.m_value, &Something::m_value 의 주소가 동일한 것이 확인되었다!
-따라서 어느것의 값을 바꾸더라도 해당 static 객체를 사용한 모든값이 같이 바뀌는것을 알수있다.
(모든 객체는 static 변수의 같은 메모리를 공유하기 때문이다!)
*주의 또 주의 : int Something::m_value = 1; 꼭, 지역이아닌 전역범위에서! 초기화하자!
( main 함수내에서 초기화 불가!)
- 정의 및 초기화는 헤더 파일 ( ooooo.h) 내에서는 불가능하다!
우리가 클래스를 구성할때 .h 그리고 .cpp 파일로 분리되는데 static 멤버 변수는 반드시 .cpp 파일에서 초기화한다!
-static 멤버 변수의 초기화는 헤더파일에서는 불가능 하다.
(Why?? 어려곳에서 헤더 파일을 include할 때 마다 static 멤버 변수를 여러번 정의 및 초기화되기때문!)
Something.h // 헤더파일예시
class Something
{
public:
static int m_value; // 클래스내에서 초기화 불가능 (선언만)
};
//전역범위
int Something::m_value = 1; //전역범위라고해도 여기는 헤더파일이다!!!! XXXXX
Something.cpp // 예시 cpp 파일
int Something::m_value = 1; // CPP파일에다가 전역이라면 ok!
static const 멤버 변수 (const = 상수선언)
static const in m_value;
- static const 멤버 변수는 클래스 내에서 초기화 하는 것이 가능 하다!
-const 이므로 값을 변경하는 것이 불가능(상수선언)하기 때문에 그 모습 그대로를 객체가 공유한다!
(초기화 시에 값이 결정되고 값은 어느곳에서도 변경이 불가능한 상수처리된다!)
(한번 초기화 한 이후로는 상수취급이기 때문에 뒤에 변경하는 것이 불가능 하다!)
-이 같은 경우는 클래스 내부에서 초기화가 가능하다.
(const는 값이 컴파일 타임에 결정 되기 때문이다!)
-따라서 헤더파일 내에서도 초기화가 가능하다!
(어차피 상수니까 계속 같은 값으로 남겨두기때문이다!)
// 코드 예시
#include<iostream>
using namespace std;
class Something
{
public:
static const in m_value; // const 선언이라 함수내에서 가능하다!
};
// int Something::m_value = 1; // const선언후에는 클래스밖에서라도 변경불가능!
int main()
{
Something st;
st.m_value = 1024; // const선언후 에는 변경 불가능!
return 0;
}
private인 static 멤버 변수 초기화
-private 한 멤버 변수면 클래스 내에서만 접근이 가능하고 외부에서는 은닉성 으로 인하여 접근이 불가능하다!
이러할 때에는 어떻게 초기화 해야할지 의문이 들 수 있으나 static 멤버는 클래스 내부에서는 초기화가 불가능하므로
private 라 하더라도 클래스 외부에서 정의가 가능하다!
//코드 예시
#include<iostream>
using namespace std;
class A
{
private: //은닉구간
static int s_value; //선언만해둔상태
};
//전역에서
int A::s_value = 1; // 은닉구간에있더라도 class A에 접근하여 정의 및 초기화 가능하다!
int main()
{
A a; //클래스 A의 객체a를 따로 생성!
cout << a.s_value << endl; // A에 직접접근하면 private 이기때문에 불가하여
//이처럼 새로운 객체를 통하여 접근이가능하다.
A::s_value = 1; // private에 직접 접근 불가능! 거기에 전역이아닌 곳에서 정의 초기화 불가능!
return 0;
}
static 멤버 변수를 클래스 내부에서 초기화 하는 방법
- inner-class 를 사용하여 클래스 내부에 클래스를 하나 더 만들고 그 클래스의 생성자 안에서 초기화를 하는 방법
- 이때 클래스에서 클래스의 클래스 객체를 static 멤버 변수로 선언해 주어야 한다.
// 코드 예시
#include<iostream>
using namespace std;
class Something
{
public:
class _init // iner class
{
public:
_init()
{
s_value = 9876; // s_value에 접근이 가능!
}
};
private:
static int s_value;
static _init s_initializer;
};
//int Something::s_value = 1234;
Something::_init Something::s_initializer;
- static 멤버 변수인 s_value 를 Something 클래스 내에서 간접적으로 초기화 하는 방법이다.
- Something 클래스 내에 _init 클래스를 만들어 준다.
- _init 클래스의 생성자에서 static 멤버 변수인 s_value 를 초기화 해준다.(간접적인 클래스 내부 초기화)
- Something 클래스의 static 멤버 변수로 _init 타입의 s_initializer 를 추가해준다.
- 클래스 외부인 전역 범위에서 Something::_init Something::s_initializer; 로 static 멤버 변수인 s_initializer 를 정의한다. (데이터 타입은 Something::_init )
static 멤버 함수
이제부터 static 멤버 함수에 대하여 알아보도록 한다.
static 멤버 함수란? static 멤버 변수 처럼 객체의 이름은 물론이고 클래스 이름만으로도 접근이 가능하다.
객체와 독립적이며 객체 생성과는 무관 하다.
따라서 멤버변수는 객체가 생성되야 메모리를 할당 받기 때문에 static 멤버 함수 내에서는 멤버 변수를 사용할수 없다!
하지만. 미리 전역에서 메모리가 할당되는 static 멤버 변수는 사용이 가능하다.
// 코드 예시
#include <iostream>
using namespace std;
class Something
{
private:
int normal_value = 99; //일반 멤버 변수
static int static_value; // static 멤버 변수
public:
static void Func() // ★ static 멤버 함수
{
int a = 1024; // Func()내부의 일반 지역 변수
cout << a << endl;
// cout << normal_value << endl; // error !! 일반 멤버 변수는 사용 불가능하다!
cout << static_value << endl; // static 멤버 변수는 사용이 가능하다!
}
};
int Something::static_value = 777; // 저역지역에서 정의 초기화
int main()
{
Something::Func(); // 객체 생성 없이 바로 클래스 이름으로 호출 가능!!
return 0;
}// 출력은 1024 그리고 777 이 출력된다.
static 멤버 함수를 사용하는 이유
객체 생성 여부와 상관없이 바로 클래스 이름으로 접근하고자 할때 주로 private 인 static 멤버 변수에 접근하려 할 때 사용!
-static 멤버 변수는 모든 객체들이 사용하고 공유해야 하는데 private 처럼 은닉되어있다면 외부에서 사용이 불가능하다.
-private인 멤버 변수들은 멤버 함수들에서만 접근이 가능 하다는 특징이 있다.
-static 멤버 함수를 통해서 private 한 static 멤버 변수에 간접 접근할 수 있도록 구현할 수 있다.
(ex . getter & setter 처럼)
// 코드 예시
#include <iostream>
using namespace std;
class Something
{
private:
static int static_vlaue; // private한 static 멤버 변수
public:
static int getValue() // static 멤버 함수
{
return static_vlaue;
}
};
int Something::static_vlaue = 777; // 저역지역에서 정의 초기화
int main()
{
// cout << Something::static_value << endl; - error !! private 이므로 직접 접근 불가!
cout << Something::getValue() << endl; // 777 출력! 클래스 이름으로 호출했을 때.
Something s;
cout << s.getValue() << endl; // 777 출력! 객체 이름으로 호출했을 때.
return 0;
}
static 멤버 함수는 this 포인터를 사용할 수 없다!
this 포인터는 객체인 자기 자신의 주소, 즉 객체의 주소를 담고 있기 때문에 객체가 생성되야지만 사용가능하다!
-static 멤버 함수는 객체들의 생성과 무관하며 언제 어디서든 클래스 이름으로도 접근이 가능해야 하기 떄문에
-static 멤버 함수 내부에서는 this 포인터를 사용할 수 없다!
-static 멤버 함수 내부에서 일반 멤버 변수르 사용할 수 없었듯이.
-이에 더해 this 포인터로 접근할 수 있는 모든 것을 할 수 없다!
(같은 객체 내의 멤버 변수나 멤버 함수는 접근 못한다. 눈에 보이지 않아도 이들은 접근 할 때에 this->가 숨어있다.)
// 코드 예시
#include <iostream>
using namespace std;
class Something
{
private:
static int static_vlaue; // private한 static 멤버 변수
public:
static int getValue() // static 멤버 함수
{
return this->static_vlaue; // 에러!!
}
int temp() // 일반 멤버 함수
{
return this->static_vlaue; // 사용 가능!!
}
};
- static 멤버 함수인 getValue() 에서는 this 를 사용 할 수 없다!
- 일반 함수인 temp() 에서는 this 를 사용 할 수 있다!
멤버 함수 포인터
일반 함수는 [함수이름]에 함수의 주소값이 들어있다는 것을 배웠다.
그러나 멤버 함수의 포인터는 조금 다르다!
- 멤버 함수 포인터는 [ &클래스이름 : : 함수이름 ] 으로 접근이 가능하다!
// 코드 예시
#include <iostream>
using namespace std;
class Something
{
public:
int temp() { return 1; }
};
int main()
{
Something s1;
Something s2;
int (Something:: * fptr_1)() = s1.temp; // error !!
int (Something:: * fptr_2)() = &Something::temp; // 접근이 가능하다!
// &Something::temp; = [ &클래스이름::함수이름 ]
}
Why??
- 멤버 변수는 각 객체들마다 따로 메모리를 가져 주소가 다르지만
- 멤버 함수는 객체마다 함수 메모리를 따로 가지는 방식이 아니기 때문이다.
- 멤버 함수는 어딘가 한군데 저장되어 있고 각 객체마다 그공간에 동일하게 접근하여 각자 다른 데이터로 사용하는방식이다.
- 따라서 일반 함수의 주소와는 다르게 멤버 함수의 주소를 받아와야 한다.
- 그러기위해서 속해있는 클래스가 어디인지 알려주어야 한다. (ex. &Something:: )
함수 포인터로 함수 실행하기 : 일반 멤버 함수
- temp()는 Something 클래스의 일반 멤버 함수 라고 가정하며
// 코드 예시
#include <iostream>
using namespace std;
class Something
{
public:
int temp() { return 1; }
};
int main()
{
Something s1;
Something s2;
int (Something:: * fptr_1)() = s1.temp; // error !!
int (Something:: * fptr_2)() = &Something::temp; // 접근이 가능하다!
// &Something::temp; = [ &클래스이름::함수이름 ]
Something s;
int (Something:: * fptr_1)() = &Something::temp;
cout << (s.*fptr_1)() << endl; // 1출력! - 함수 포인터로 s 객체의 temp 함수 실행!
}
- int (Something:: * fptr_1)()
포인터 선언시 Something:: 를 꼭 붙여 주어야 한다.
함수 포인터로 일반 멤버 함수를 실행할 때는 꼭 (s.*fptr_1)() 처럼 객체로 접근해 주어야 한다.
fptr_1 은 객체에 종속되는 일반 멤버 함수를 참조하고 있기 때문에 단독으로 호출할 수 없기 때문이다.
멤버 함수 포인터는 객체의 내용물을 참조하기 때문에 (객체.*멤버함수포인터)(매개변수) 로 사용해야 한다.
포인터로 객체를 참조하고 있다면 (객체포인터->*멤버함수포인터)(매개변수)
함수 포인터로 함수 실행하기 : static 멤버 함수
- getValue()는 Something 클래스의 static 멤버 함수 라고 가정하며
// 코드 예시
#include <iostream>
using namespace std;
class Something
{
public:
int temp() { return 1; }
};
int main()
{
Something s1;
Something s2;
int (Something:: * fptr_1)() = s1.temp; // error !!
int (Something:: * fptr_2)() = &Something::temp; // 접근이 가능하다!
// &Something::temp; = [ &클래스이름::함수이름 ]
Something s;
int (Something:: * fptr_1)() = &Something::temp;
cout << (s.*fptr_1)() << endl; // 1출력! - 함수 포인터로 s 객체의 temp 함수 실행!
int(*fptr_2)() = &Something::temp;
cout << fptr_2() << endl; // 함수 푄터로 s 객체의 temp 함수 실행
}
- int (*fptr_2)()
static 멤버 함수는 객체와 무관하게 연산이 이루어 지기 때문에 일반 함수 포인터로 취급 받는다.
따라서 포인터 선언시 Something:: 을 떼고 일반 함수처럼 int(*fptr_2)() 로만 선언해 준다.
fptr_2() 일반 함수 포인터로 취급되기에 이렇게 단독 호출할 수 있다.
'공부' 카테고리의 다른 글
C++ 생성자 & 소멸자 (0) | 2022.11.23 |
---|---|
C++ 문자>값으로 / 값>문자로 변형 참고 (0) | 2022.11.23 |
C++ 다수 컴퓨터 가위 바위 보 게임 (0) | 2022.11.17 |
C++ 이전과제 클래스구조로 변환시키기 (0) | 2022.11.16 |
C++ 블랙잭 게임 복습 (1) | 2022.11.16 |
댓글