const
const 는 상수라는 뜻 이다.
상수라는 것은 프로그램이 시작되면서 끝날때 까지 절대 변하지 않는 값을 의미
하지만 진정한 의미로서의 const는 define 매트로이며,
const 키워드를 이용해 만든 변수는
상수이지만 '저장공간'을 갖고 있는 변수이며
실질적으로 프로그램 내부에 박혀있는 값은 아니다.
클래스 내부에서의 const 멤버
이 const라는 것이 실제로 변수로서 저장공간을 갖고 있다는 것을 확인하는 방법은
클래스 멤버를 const로 선언하는 것이다.
이렇게 const로 선언된 클래스 멤버 변수는
선언과 동시에 초기화를 하거나, 클래스 생성자에서 초기화를 해줘야 한다.
생성자에서 초기화를 해 줄 때에는
변수이름 (초기값) 과 같은 방법을 이용해 초기화를 한다.
#define _CRT_SECURE_NO_WARNINGS #include <cstdio> #define MYVALUE 100 class Person { public: const int age; // Person은 const int age를 꼭 가져야만 초기화 가능, age가 상수이기 때문 // 정의가 되지 않은 상수는 존재 할 수 없다 Person() : age(10) { // 생성자 // const라는 클래스 멤버변수가 있을 때 이 멤버변수는 꼭 생성자에서 멤버변수를 초기화 해줘야한다 // age값이 10으로 초기화 } }; int main() { const int value = MYVALUE; // MYVALUE라고 하는 이 토큰이 100이라는 값으로 매크로에 의해서 치환 // value는 변수(프로그램에 직접 박히는게 아님)고 MYVALUE는 상수(프로그램에 직접 박혀있음) Person* p = new Person(); // 생성자에 멤버 변수 초기화 안하고 쓰면 문제가 있는 코드 printf("%d", p->age); // 10이 출력된다. printf("%d", value); // 100이 출력된다. return 0; } |
age가 변수 공간을 갖고있다는 것은 상수 값 이외의 변동형 값(변수 형태)을 받아 가변적인 상수가 될 수 있다는 것
Person( int n ) : age(10) {
// n이라는 변수를 age에다가 집어 넣어 줄 수가 있다, 10이란 값은 절대 변하지 않음
age의 값은 모르지만 사용자로부터 입력을 받을 수도 있다 (int input;)
#define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> // strcpy 사용 #define MYVALUE 100 class Person { public: const int age; // 변수 형태의 공간을 갖고 있는 상수 // 즉, 프로그램에 박혀있는 정해져있는 숫자는 아닌데 const 변수가 생성이 되면 변수 공간을 가지고 있지만 그게 강제적으로 바꿀 수 없다 // Person은 const int age를 꼭 가져야만 초기화 가능, age가 상수이기 때문 // 정의가 되지 않은 상수는 존재 할 수 없다 Person(int n) : age(n) { // 생성자 // const라는 클래스 멤버변수가 있을 때 이 멤버변수는 꼭 생성자에서 멤버변수를 초기화 해줘야한다 // int n 이라는 Person(int n)을 받아가지고 n이라는 int값을 const int에 집어넣을 수 있다 // 그러면 집어넣은 순간부터 age 값은 바뀔 수 없다 하지만 값을 집어넣을 수는 있다 } }; int main() { const char* str; // 박혀있지만 그 주소값을 가지고 있는 const char 포인터이기 때문에 const char 포인터에 char포인터 할당 가능 char* myStr = new char[100]; strcpy(myStr, "Hello World\n"); str = myStr; // char포인터가 const char 포인터가 될 수 있었던 원인은 위 age와 마찬가지 printf("%s", str); return 0; // 밑에 있는 내용 실행 안됨 int input; fseek(stdin, 0, SEEK_END); scanf("%d", &input); Person* p = new Person(input); printf("%d", p->age); return 0; } |
define 된 값은 프로그램 상에 아예 박혀버린다. 집어 넣는 숫자들도 프로그램 상에 아예 박혀버린다.
다만 const라는 키워드를 활용해서 변수를 만들었을 때는 (const int age;) 상수긴 한데
정확하게 프로그램에 박혀버린 상수는 아니다. 변수의 공간을 갖고 있다
static
static 이라는 것은 ' 정적 ' 이라는 뜻이다.
정적인 어떤가 라는 뜻 이며, 유일하고 독립적인 어떤 것을 의미
이 static은
class의 내부에 존재할 경우
class 내부에서 유일하고 독립적인 멤버라는 뜻이 되며
class 외부에 존재할 경우
obj 파일 (오브젝트 파일) 내부에서 유일하고 독립적인 존재 라는 뜻
(글로벌 이라는 뜻이 아니다. 절대로, 그렇게 생각하면 안된다.)
static 함수는 dynamic 함수(static 함수와 반대, 동적인 함수) 와 가장 큰 차이점이
this 라는 키워드의 존재 유무
dynamic 함수는 this 키워드 존재 가능 (void foo())
static 함수는 this 키워드가 존재할 수 없다 (static void goo())
이유 : 이 함수를 호출하는 주체가 없다. 주체가 되는 클래스 인스턴스가 존재하지 않는다.
static 함수는 멤버변수에 접근 할 수 없다.
단, 접근할 수 있는 멤버변수가 하나 있으면 static 멤버변수에는 접근할 수 있다.
static 이라는 변수타입을 전역 변수라고 생각하여 많이 쓰인다. (좋은 프로그램이 습관은 아님)
#define _CRT_SECURE_NO_WARNINGS #include <cstdio> #define MYVALUE 100 class MyClass{ public: static int b; int a; void foo(){ // static의 반대 동적인 dynamic 함수 a = 100; this->a = 200; // 정상적으로 작동 printf("foo\n"); // dynamic 함수 특징 : 자기 자신의 인스턴스가 있음 // foo()라는 함수를 부르는 인스턴스가 있다 // void foo() 함수는 행동이고 이 행동이라는 함수는 행동을 하는 주체가 있다 // dynamic 멤버 변수는 static 멤버 변수에 마음대로 접근 가능 } static void goo(){ // 정적 함수, 즉, static 함수 printf("goo\n"); // goo()라는 함수는 주체가 필요 없다 그냥 정적으로 존재 // 클래스 안에 존재하는 클래스 인스턴스가 없는 함수 // a 에 접근할 수 없다 // 주체가 없이도 실행할 수 있는 이 goo라는 함수는 a을 알 수도 특정할 수도 없다 // 그래서 a에 100을 할당 할 수 없고, this란 키워드를 사용할 수도 없다 // static int b (static 멤버변수)에는 접근할 수 있다. 둘 다 주체가 없기 때문 } }; int main() { MyClass* c = new MyClass(); // foo 라는 함수 호출을 위한 클래스 만들기 c->foo(); //c->goo(); // 정상 실행 클래스를 만들지 않아도 됨, 주체가 있어도 되고 없어도 되기 때문에 // 두번째 방법 MyClass::goo(); // 이런 방법으로도 호출 가능, 원래대로라면 이게 맞다 return 0; } |
static 멤버변수는 일반 멤버함수들이나 멤버변수를 사용하는 것과는 약간 다름
Header.h
#define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> class MyClass{ public: static int static_member_int; // static 멤버변수는 소스파일에 따로 몸체를 만들어줘야한다. int a; void foo(); // dynamic static void goo(); // static // 둘 다 함수 몸체를 만들 때는 몸체에다 void만 붙임, static은 붙이지 않는다 }; |
prc.cpp
#include "Header.h" int main() { MyClass::static_member_int = 100; printf("%d", MyClass::static_member_int); // cpp파일에 몸체 안만들시 오류 // 이유 : 이 클래스가 갖고있는 멤버 변수들은 클래스가 만들어지면서 하나하나 선언이 되면서 거기에 메모리 공간을 갖게 되지만 // static 멤버변수 같은 경우는 클래스와 관계 없이 일단 존재해야 되는 변수이기 때문에 어떤 위치에 이 메모리 공간을 할당 해 줘야함 // 이 메모리 공간에 할당된 변수를 우리가 직접 하나 만들어줘야한다. return 0; } int MyClass::static_member_int = 1; // 몸체 만들어주기 (Linking 해주는 것) void MyClass::foo() { // 함수 몸체 (void 붙이기) } void MyClass::goo() { // 몸체를 만들땐 static 키워드 필요하지않음 } |
extern
: 프로그램 전체에서의 static
extern 키워드는 '외부에 존재한다'는 뜻 이다.
어떻게 보면 C/C++ 에서 obj 파일로 링크가 되면서 만들어지는
진정한 전역 변수를 만들어줄 수 있는 키워드라고 볼 수 있다.
이 extern 키워드의 활용과 뜻은 굉장히 많지만
초보적인 내용에서는 '전역변수'를 만들어준다.
클래스에서 내부의 변수를 static으로 선언을 함으로써 유일무이한 변수 즉 전역적인 변수를 만들 수 있다
하지만 extern 키워드를 사용하면 프로그램 전체에서 동작하는 전역적인 변수를 만들 수 있게 된다
테트리스 코드의 Header.hpp에서 선언
extern int displayData[GRID_HEIGHT][GRID_HEIGHT]; // 선언
// C++ 컴파일러는 이 선언을 보고 이 선언에 따른 디스플레이 데이터에 관한 변수가 어디에 있겠지 하면서 찾아간다
// 헤더파일의 displayData는 display클래스에서도 쓰이고 gameEngine클래스에서도 쓰기 때문에 전역적인 변수로 선언을 해줘야만 사용이 편리하다
// 그래서 extern 키워드를 사용하여 프로그램 전체에서의 전역적인 변수를 만들어 사용하였다.
테트리스 코드의 Source.cpp에서 몸체
int displayData[GRID_HEIGHT][GRID_HEIGHT] = { 0, }; // 정의
// 선언을 보고 변수를 찾은 컴파일러는 링크를 걸어준다.
// 그 때 전역변수로서 이 header.h를 include하면 displayData파일에서는 어떤 cpp파일에서도 접근 가능한 유일무이한 변수가 된다
Header.h
#define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> static int myValue = 100; // static변수는 각각의 cpp파일에 따로따로 저장되는 변수 // 때문에 sub.cpp파일에서 올린 1의 값은 sub.cpp 파일 안에 myValue라는 변수의 값이 1 증가된 것 // Source.cpp 파일은 Header.h파일을 include한 이 static myValue라는 값이 들어온 것 // static을 빼면 오류 // static이라는 값을 변수 앞에 붙이게 되면 이 변수가 각각 cpp파일, 즉 컴파일 될 때 오브젝트 파일에 종속적인 변수명이 된다. // static 함수도 마찬가지 // static 함수의 원형은 각각의 include한 소스파일에 전부 다 존재해야한다. void add(); // 선언 |
Sub.cpp
#include "Header.h" void add() { myValue++; } |
Source.cpp
#include "Header.h" int main() { add(); printf("%d", myValue); // 100이 나온다, add라는 함수로 값을 1 증가시켰으면 101이 나와야함 // Source.cpp에 add라는 함수가 존재하지 않기 때문에 return 0; } |
클래스 외부의 static의 특징
static이라는 것이 붙어 있으면 이것이 각각의 cpp 파일 마다 다르게 작용한다.
include한 cpp 파일 마다 다른 선언과 다른 몸체를 갖게 된다. 그래서 클래스 외부에서 잘 사용하지 않음
원하는 동작을 하기 위해서는 static 앞에 extern 키워드를 써준다.
extern static으로 선언하고 cpp에 몸체를 만들어준다.
extern으로 해주게 되면 어떤 cpp파일에서 접근해도 동일한 형태의 변수에 접근할 수 있다.
Header.h
#define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> extern int myValue; // 선언 // 이 소스코드 전체에 obj파일이 어딘지는 모르겠는데 myValue라는 어떤 변수가 있어서 찾아보면 나온다는 뜻 void add(); // 선언 |
Sub.cpp
#include "Header.h" int myValue = 100; // 몸체 // extern int myValue; 선언과 변수명이 일치해서 연결됨 void add() { myValue++; } |
Source.cpp
#include "Header.h" int main() { add(); add(); printf("%d", myValue); // 102가 출력된다 return 0; } |
요약
const : 가변적인 상수, 한 번 수를 받으면 프로그램 끝까지 유지하지만 받을 때 변수로 받을 수 있음
static : 클래스 외부와 내부에서의 용도가 다름
- 클래스 내부 : 그 클래스가 가지고 있는 유일무이한 변수 또는 함수,
static이 아닌 변수는 여러가지가 있을 수 있지만 static은 그렇지 않고 단 하나만 존재
- 클래스 외부 : 그 파일 내에서 선언되는 변수
다른 파일에서도 사용은 가능하나 따로따로 몸체를 만들어줘야함
extern : 프로그램 전체에서 사용되는 변수, 선언 후 몸체를 따로 만들어야 하지만 어떤 파일에서도 접근할 수 있는 변수
'C++' 카테고리의 다른 글
c++ 15 ( 기본 자료구조 - STL 기초 자료구조 활용 ) (0) | 2022.02.23 |
---|---|
c++ 14 ( Generic 이라고도 불릴 수 있는 개념 ) (0) | 2022.02.22 |
c++ 12 (문자열과 네임스페이스 그리고 레퍼런스 타입) (0) | 2022.02.17 |
c++ 11 (실행구조 / Bitwise / 순수 가상함수) (0) | 2022.02.17 |
c++ 10 (C++ 의 컴파일에 관하여 - LNK 오류에 대처하기) (0) | 2022.02.14 |