프로그래밍 언어/C & C++ 정리

20230130 그간 배운 변수와 함수 상수 등 정리

게임 개발 2023. 2. 5. 16:27

 

1-1) 지역 변수(=Local Variable) 란?

 

지역 변수의 정의는, 초기값이 변수에 설정되고 함수나 메서드 또는 블록에서 사용되는 것이다.

이 변수는 오직 함수나 메서드 혹은 다른 블록으로 넘어 갈때,

 

즉, 지역 변수가 살아있던 블록이나 메서드 혹은 함수가 아닌 다음 블록이나 메서드 함수에서는

자연적으로 파괴되어야만 유의미한 변수이다.

지역 변수가 해당된 함수, 메서드, 블록말고 다른 외부에서 참조되는 경우 프로그램은 오류를 반환한다.

 

전역 변수와 지역 변수는 같은 이름을 사용할 수 있으나, 혹은 같은 지역변수라 하더라도

더 영역이 넓은 블록에 있는 같은 이름의 지역 변수와

그 영역 내에 더 좁은 영역의 블록의 지역 변수가 같을 수 있다.

 

하지만 이들을 구별하는 것은 변수가 작동하는 경계 범위에 따라

지역변수냐 전역변수냐 더 좁은 범위의 지역변수를 식별하여 컴퓨터는 연산한다.

 

1-2) 지역변수의 컴퓨터 언어의 문법

 

지역변수는 기본적으로는, 지역 변수의 정의와 선언에 의해 존재된다.

지역 변수의 정의와 선언은 다음과 같은 원칙에 부합하여야 한다.

 

1. 로컬 변수 이름

2. 데이터 형 ( = 자료형 )

3. 초기값

 

초기값은 정의할 때 한 문장으로 선언하거나,

별도의 문장으로도 선언할 수 있다.

 

만약 초기값을 해주지 않으면 쓰레기값이 들어가게되서

원하는 값을 도출할 수 없다.

 

모든 정의문은 반드시 세미콜론으로 끝나야 하며, 그렇게 끝내지 않으면 오류가 발생한다.

 

이로써 다양하거나 동일한 데이터 타입을 가진 여러 지역 변수를 함수 내에서 이용하며

함수나 블록 메서드 내에서

단일 문으로 결합을 하여 코드에 이용한다.

	data - type local - variable - name = initial - value;
	int rate = 400; char empname = 'XYAZ', option = '1';
	rate, empname, option are local variable names.
	int, char are data types.
	400, 'XYAZ', '1' are initial values
 

 

이를 코드화 하면, 다음과 같다.

#include <iostream>

using namespace std;

void For_EX()
{
	int rate = 400;
	char empname[80] = "XYAZ";

	cout << "다음 비율의 이름은 \" " << empname << " \" 이고, " << endl
		<< "다음 비율의 값은 \" " << rate << " \" 이다." << endl;

	cout << "이를 printf 혹은 cout 해보면 다음과 같다." << endl << endl;

	cout << empname << " : " << rate;
}

int main(void)
{

	For_EX();

	return 0;
}
 

또한 초기값은

 

프로그램 실행 시작 시 유지하도록 정의된 값이며

정의된 데이터 형식과 같아야한다.

 

ex)

int a = 3.125845; => 3만 나온다.

 

잘못된 할당은 오류로 처리된다.

 

1-3) 지역 변수의 이름

 

1. 항상 알파벳 또는 "_" 밑줄로 시작해야 한다.

2. 숫자 문자로 시작해서는 안 된다.

3. 대소문자를 구분하며 대문자와 소문자가 있는 이름은 두 가지 다른 변수로 간주된다.

4. 공백, 그래픽 기호 및 "_" 가 아닌 다른 특수 문자를 포함하여서는 안된다.

5. 변수명은 기능, 방법, 블록 내에서 중복되면 안된다. (& 조건임 || 조건 아님)

6. 키워드를 변수 이름으로 사용할 수 없다.

 

가능한 이름 = studentname, PONO, _supplier, PaRTno

 

불가능한 이름 = Order@no, marks#, 788supplier

 

 

1-4) 결론적으로 C++ 내에서 지역 함수는 어떤 일을 하는가?

 

지역 변수는 함수 또는 블록이나 메소드 내에서 정의되며

그 작동 범위는 장치 내로 제한되며,

범위 밖의 결과를 저장 하는 데 참조하거나 사용함이 가능하지 않다.

지역 변수는 해당 기능이 실행될 때 활성화되고, 이 변수들을 사용하여 모든 작업의 수행에 따라

프로그램 흐름에 따라 이 변수들을 저장한다.

 

기능을 다한 지역변수는 메모리에서 삭제되고

 

지역 변수는 함수 정의에서 변수로 정의되거나, (= 매개변수로)

함수 본문에서 별도로 정의 할 수 있다

#include <iostream>
using namespace std;
int average1(int totalmarks, int students)
{
     return totalmarks / students;
}
int main() 
{
     int tm = 600, s = 50;
     int avg;
     avg = average1(tm, s);
     cout << "The Average marks in the class is : "<< avg << endl;
     return 0;
}
 

 

 

 

2. 지역 상수(Local constant)

 

지역 상수를 설명하기 전에

 

프로그램에서는 다음 두 가지 방법으로 상수를 정의할 수 있다.

 

첫째는, #define 전처리기를 사용하여 상수화 하는 것이고

둘째는, const 키워드를 이용하여 상수화하는 것이다

 

여기서 말하는 지역 상수는 const를 사용하여 상수화한 지역 상수를 말하는 것이다.

 

앞서 말한 지역 변수에 const를 붙이면 상수화 된다

 

그럼 이러한 개념으로 리터럴 상수와 심볼릭 상수를 설명하겠다.

 

2-A) 리터럴 상수

 

리터럴 상수라는 것은 상수화된 변수가 읽는 그대로의 의미가 있는 것들을 Literal 상수라 한다

 

Literal의 영어 뜻이

문자 그대로의 라는 뜻을 포함하는데 이는 가장 기본적이고 일반적인 문자 그대로의 뜻을 의미한다.

 

아래의 표는 C언어와 C++ 언어의 리터럴 상수 타입의 차이이다.

 

사진 삭제

사진 설명을 입력하세요.

리터럴 상수는 크게 4가지로 나뉜다

 

첫 번째는 정수형 상수이고, 그 다음은 실수형 상수, 이후는 문자 상수, 마지막으론 문자열 상수가 있다.

 

2-A-a)1. 정수형 상수

정수형 상수는 간단히 말해

int x = 1
 

이러한 코드가 있을 때,

 

1은 리터럴 상수에서 정수형 상수이다

 

우리가 흔히 쓰이는 a,A도 또한 아스키 코드로 이따 문자 상수에서 할 것이지만

이도 리터럴 상수이다.

 

정수형 상수란 말은 말 그대로 소수점이 붙지 않은 정수형의 상수라는 뜻이다.

 

2-A-b) 실수형 상수

 

실수형 상수란 123.45와 같이 소수점을 포함하는 상수를 말한다.

double y = 2.356
 

위 예시에서 나오는 뒤에 나오는 2.356 또한 리터럴 상수이다.

 

2-A-c) 문자 상수

 

작은 따옴표로 묶은 문자 하나를 문자 상수라 한다.

 

문자 상수란 작은 따옴표 ( = ' ' ) 로 묶은 문자 하나를 의미하는데

 

예시로는 아까 말한 아스키 코드의 A,B,C,a,b,c 등과 같은 우리가 사용하는 알파벳

위 예시 속의 x나 y또한 문자 상수이다 즉, 리터럴 상수이다.

 

*,+,= 도 또한 키보드로 표현 가능한 영문자와, 특수 기호들이 바로 문자 상수이다.

 

또한 위에서 말한 아스키 코드 또한 리터럴 상수의 하위 개념인 문자 상수이다.

아래는 아스키 코드 표이다.

사진 삭제

사진 설명을 입력하세요.

 

 

2-B) 심볼릭 상수

 

심볼릭 상수는 프로그래밍 소스 코드에서 어떠한 상수를 symbol화

즉, 이름 그대로 상징적인 의미를 부여하여 사용하는 상수를 뜻한다.

 

이런 심볼릭 상수를 사용하는 방법은 크게 3가지가 있다.

 

2-B-a) 전처리기를 이용한(#define) 심볼릭 상수

 

맨 처음 설명한 전처리기를 이용한 심볼릭 상수를 만드는 방법이다.

#define ITEM_DEFAULT_ARMOUR 150;
 

다음과 같이 선언하면 어떤 코드에서 TEM_DEFAULT_ARMOUR 를 입력하면 자동적으로 150이 들어간다.

 

이는 TEM_DEFAULT_ARMOUR의 상징적 의미가 150이 된 것이다.

 

다음은 내가 흔히 이용하게 될 상수인데,

꼭 기억에 계속 남겨 두면 좋겠다.

 

2-B-b) const 키워드를

 

const 키워드를 사용해서 변수를 상수화 시켜 사용하는 방법이다.

 

포인터를 예시로 const symbolic 상수화를 설명하겠다.

 

int Default_Address = 5

// 예시 1
const int *Temp = &Default_Address
 

다음과 같은 예시 1번은 내가 가르키고 있는 원본을 상수화 한 것이다

이는 int Default_Address = 5의 값이 5말고 다른 값을 가지지 못하게끔 한 것이다.

 

즉, const의 범위가 int *Temp 까지 미친다는 것이다

 

이를 내가 알기 쉽게 표현하자면

 

const ( int *Temp ) 이다.

 

컨스트가 int *Temp 를 모두 포함하고 있다는 것이다.

 

int Default_Address = 5

// 예시 2
int * const Temp = &Default_Address
 

그럼 예시 2번은 무엇일까?

 

컨스트의 범위가 Temp로 좁혀졌다. 이는

Temp의 값인 &Default_Address 를 변동없이 그대로 가져가겠다는 것이다.

 

이처럼 const는 어디에 붙냐에 따라 symbolic되는 지정되는 범위가 다르다.

때문에 지정되는 범위가 이처럼 두 곳이라면 두 곳다 const를 붙일 수 있다.

 

int Default_Address = 5

// 예시 3
const int * const Temp = &Default_Address
 

이처럼 범위를 지정하여 const 하는 것이 가능하다.

 

그렇다면 레퍼런스를 사용하면 어떻게 될까?

나도 모르겠다 선생님께 내일 여쭤보기

 

int const& const Another_Reference = Default_Address;

에서 int const 를 하면 값이 변경이 안되고

const를 하면 가리키는 값이 변경 안되나보다. 근데 나도 잘 모르겠다.

일단 포인터랑 비슷한 것 같다.

 

int main(void)
{

	int Default_Address = 5;

	int const& const Another_Reference = Default_Address;

	cout << Default_Address << endl;

	cout << Another_Reference << endl;

	Another_Reference = 9;


	return 0;
}
 

마지막으로는 enum 으로 심볼 상수를 만드는 것이다.

 

이는 다음 예제와 같다.

 

enum Weapon {long_sword = 100, short_sword = 200}

int main()
{
    cout << "long_sword : " << long_sword << endl;
    cout << "short_sword : " << short_sword << endl;

    return 0;
}
 

 

enum의 값을 미리 정해주고

main문에서 다음과 같이 쓰면 심볼릭 상수가 된다.

 

 

 

3. 전역 상수

 

전역 상수란?

 

위에서 말한 상수들을 어떠한 함수나 메서드, 블록 내에 위치시키지 않고

전역에서 사용 가능한 공간에 위치시켜 이 상수를

생명 주기를 static과 같은 data 영역에 저장시켜

계속해서 사용할 수 있게 만든다.

단 전역 변수와 다른 점은 상수이므로 값의 변경이 불가능하다.

 

 

 

4. 전역 변수

 

전역 변수란?

 

전역 변수는 정적 주기로 (static duration) 프로그램이 시작할 때 생성되고 프로그램이 종료될 때 파괴된다.

이는 지역변수와 다른 생명주기를 가지고 있다.

그리고 앞서 말한 정적변수 static과 같은 생명 주기를 지니고 있다.

 

전역 변수는 지역 변수와는 다르게 전체에서 사용 한다는 의미로

어떠한 특정 지역인 중괄호 밖에 선언된 변수를 말한다.

 

이러한 변수는 지역과 관계 없이 어느 곳에서든 유효하며, 사용 가능하다.

 

 

 

5. 정적 전역 변수

 

C++에서 정적이라는 것이 붙고 전역 이라는 말이 붙는다면 일단 DATA 영역에 memory가 저장된다.

 

정적 전역 변수는 (static global variable)은

자신이 선언된 소스 파일에서만 사용 할 수 있고, 외부에서는 사용이 불가능하다.

 

정적 전역 변수의 예시

 

/* print.c */
#include <stdio.h>
static int num1 = 10;

void printNumber(){
    printf("%d\n", num1);    // 10: 정적 전역 변수 num1의 값 출력
}


/* main.c */
#include <stdio.h>
extern int num1;    // 다른 소스 파일(외부)에 있는 정적 전역 변수는 extern으로 사용할 수 없음
                    // 컴파일 에러

int main(){
    printf("%d\n", num1);     // 정적 전역 변수 num1의 값 출력
    return 0;
}


/* 컴파일 결과값 */
fatal error LNK1120: 1개의 확인할 수 없는 외부 참조입니다.
 

 

extern 키워드로 선언한 외부 변수는 외부 파일에 선언된 변수를 참조하는 외부 변수이다.

여기서 외부 파일에 선언된 변수는 전역변수이다.

 

그러나 static으로 선언된 변수는 자신의 파일에서만 접근할 수 있어서 컴파일(링크) 에러가 난다.

사진 삭제

사진 설명을 입력하세요.

<사진출저 코딩 도장 https://dojang.io/mod/page/view.php?id=690>

 

puls 개념 ++++++++++++++

++ extern의 역할 : 다른 파일에서 이미 이름이 같은 전역변수가 선언이 되었다는 의미이다.

즉, 다른 파일간의 변수를 공유하고 있다라는 뜻이다.

 

// in main.cpp file
int externVariable = 1;
static int staticGlobalVariable = 0;
 
// in header.cpp file
extern int externVaribale;
static int staticGlobalVariable = 1;
 

static이 붙어있는 변수의 경우

 

1. 현재 파일 내에서만 존재하는 파일의 지역변수 취급을 받기 때문에 이름이 같더라도 서로 다른 파일에서

각각 다른 변수로 취급이 된다.

 

2. 따라서 static 변수를 통해 혹시 모를 전역변수의 이름이 같게되는 문제를 조금이나마 완화할 수 있다.

 

 

extern 이 붙어있는 변수의 경우

 

1. 정말 global 변수라는 뜻으로 사용되며 다른 파일에 같은 이름의 전역변수가 존재한다는 것이기 때문에

이 정보를 컴파일러에 전달하고 main.o 와 header.o 를 링킹해준다.

 

2. 진정한 전역변수라 볼 수 있다.

 

3. extern 키워드는 전역변수가 외부에 있다는 것을 표시만 할 뿐이고 선언을 하지는 않는다.

따라서 어디엔가 정의되어있지 않으면 링크에러가 발생할 것이다.

 

4. extern 은 다른 파일의 함수나 변수를 가져오는 것 이외에도 현재 파일 내에서도 작동이 가능하다.

현재 파일에서 작성중인 코드에, 현재 스코프까지 사용하고자 하는 함수가 없는 경우

extern 키워드를 적절히 사용하면 된다.

  // 예시 1
  extern int myFunction();
  int main(){
  	myFunction();
  }
  int myFunction(){
  	//do something
  }
 

다음 예시 1번과 같이 말이다. 이런 식으 사용이 가능하며 myFunction 이 어디엔가는

정의되어있다는 것을 알려주는 키워드이다.

 

 

6. 정적 전역 상수

 

정적 지역 변수를 상수화 하여 사용한다면 이는 정적 전역 상수가 된다.

 

7. 정적 지역 변수

 

정적 지역 변수

#include <iostream>
using namespace std;
void func() 
{
   static int num = 1;
   cout <<"Value of num: "<< num <<"\n";
   num++;
}
int main() 
{
   func();
   func();
   func();
   return 0;
}
 

다음 코드를 보면 알 수 있듯이 결과물은 이러할 것이다.

 

Value of num: 1
Value of num: 2
Value of num: 3
 

자, 그럼 이제 이 프로그램을 이해를 해보자.

 

함수 func()에서 num은 한 번만 초기화가 되는 정적 변수이다.

그런 다음 num 값이 표시되고 1씩 증가하게 된다. 이에 대한 코드의 정보는 다음과 같은데,

 

void func() 
{
   static int num = 1;
   cout <<"Value of num: "<< num <<"\n";
   num++;
}
 

이러한 정보를 가진 함수가 3번 호출하겠다고

메인문에서 선언하면

 

static을 사용하지 않은 지역변수는 늘 1일 것이나,

생명주기가 긴 static을 사용함으로

 

num이 1에서 다시 호출 되면 2 또 다시 호출되면 3으로 바뀌어 있는 것이다.

 

 

8. 정적 지역 상수

 

정적 지역 변수를 상수화하여 사용한다면 이는 정적 지역 상수가 된다.

 

9 .맴버 변수

 

객체 지향 프로그래밍에서, 맴버 변수는 특정 객체와 연관되어 있으며,

모든 메서드 (멤버 함수)에 접근할 수 있는 변수이다.

 

클래스 기반 프로그래밍 언어에서는 변수의 복사본이 클래스의 모든 인스턴스와 공유되는

클래스 변수(정적 멤버 변수 = 정적 지역 변수)와 클래스의 각 인스턴스가

변수의 독립적인 복사본을 갖는 인스턴스 변수의 두 가지 유형으로 구분된다.

 

 

puls 개념 +++++++++++++++++++

 

instance 사례 경우 라는 뜻의 영단어로

인스턴스는 일반적으로 실행 중인 임의의 프로세스, 클래스의 현재 생성된 오브젝트를 가리킨다.

객체(오브젝트)의 인스턴스는 데이터베이스나 SGA, 백그라운드 프로세스 등 광범위한

컴퓨터 시스템 자원의 접근에 할당된 물리 메모리의 일부를 가리킨다.

테이블 인스턴스( 또는 데이터베이스 인스턴스) : 데이버베이스 설계의 개념이다.

종종 컴퓨터나 수학 등에서 인스턴스는 사전적 의미로서 일반적인 경우에 대한 실제적인 특정 상황으로 실현된다.

 

++ 꼬리 물기 개념

 

객체 (오브젝트) 의 인스턴스에서 객체

 

컴퓨터 과학에서 객체 또는 object는 클래스에서 정의한 것을 토대로 메모리(실제 저장공간)에 할당된 것으로

프로그램에서 사용되는 데이터 또는 식별자에 의해 참조되는 공간을 의미하며,

변수, 자료 구조, 함수 또는 메소드가 될 수있다. 프로그래밍 언어는 변수를 이용해 객체에 접근하므로

객체와 변수라는 용어는 종종 함께 사용된다. 그러나 메모리가 할당되기 전까지는 객체는 존재하지 않는다.

 

절차적 프로그래밍에서 하나의 객체는 자료나 명령을 포함할 수 있지만 두 가지를 동시에 포함하지는 않는다.

(명령은 프로시저나 함수의 형태를 가진다.)

 

객체지향 프로그래밍에서 객체는 클래스의 인스턴스이다.

클래스 객체는 자료와 그 자료를 다루는 명령의 조합을 포함하여 객체가 메시지를 받고 자료를 처리하며

다른 객체로 보낼 수 있도록 한다. 실세계의 비유로 설명하자면, 가령, 어떤 사람이 집에서 살기를 원할 때,

그 집의 청사진(집의 설계도)이나 축소 모형 따위는 전혀 필요가 없다.

필요한 것은 설계에 맞는 실제 집이다. 이 비유에서 청사진은 클래스를, 실제 집은 객체를 나타낸다.

 

++ 나의 이해

 

클래스는 단순한 이러한 용도로 쓸 것이다 라는 선언이며

실제 객체를 가지고 응용하고 이용하는 것이다.

 

10. 맴버 상수

 

멤버 변수에 const를 붙이면 멤버 상수이다

class Point
{
public:
    int x, y;
    void print() const { cout << x << ", " << y << endl;}       // print() 는 x, y를 변경시키지 않는다.
    void set(int x, int y) const {this->x = x; this->y = y;}  // error. x, y가 변한다.
};
 

 

여기서 유의해야할 점은

 

상수 객체는 상수 함수만 부를 수 있다.

때문에 멤버 상수를 쓰려면 상수 함수가 필요하다.

#include <iostream>
using namespace std;
 
class Point
{
public:
    int x, y;
    void print() const { cout << x << ", " << y << endl;}       // print() 는 x, y를 변경시키지 않는다.
    void set(int x, int y) {this->x = x; this->y = y;}    // error. x, y가 변한다.
};
 
int main() 
{
    const Point p1{1, 2};
    p1.print();     // OK
    p1.set(3, 4);   // ERROR
 
    return 0;
}
 

 

만일 line 8에서 print() 함수에 const 키워드가 없다면 "error : passing 'const Point' as 'this' argument disc ards qualifiers

[-fpermissive]" 이란 에러가 나게 된다.

이는 미리 컴파일 된 소스에 해더만 가지고 가져다 쓸 경우, 멤버 함수 선언부에 const 가 없다면 컴파일러가

체크할 수 없기 때문이다.

 

변수를 함수 인자로 전달할 때, 일반 변수로 전달하면 복사생성자가 불리게 되는데

 

클래스의 경우는 성능 저하가 크게 되므로, 값을 변화시키지 않고 전달하려면

const 참조를 통해 전달하는 것이 일반적인 경우이다.

 

이때 const 참조로 가져온 변수는 멤버 함수 중에 상수 멤버 함수만 실행할 수 있고,

따라서 성능 향상을 위해 const 참조로 인자를 전달하기 위해선 멤버값을 변화시키지 않는 함수는

상수 멤버 함수로 구현해야 한다.

 

#include <iostream>
using namespace std;
 
class Point
{
public:
    int x, y;
    void print() const { cout << x << ", " << y << endl;}       // print() 는 x, y를 변경시키지 않는다.
    void set(int x, int y) {this->x = x; this->y = y;}    // error. x, y가 변한다.
};
 
void foo(const Point& p)
{
    p.print();      //OK
    p.set(2, 3);    //ERROR
}
 
int main() 
{
    Point p1{1, 2};
     
    foo(p1);
 
    return 0;
}
 

상수 멤버 함수에서는 모든 것이 const 취급이기에, 참조 return도 가능하지 않고, const 참조만 리턴이 가능하다.

 

위 코드와 같이 이름이 같은 함수를 const 버전과 일반 버전 둘 모두 제공할 수 이따.

이때, 객체가 상수면 상수 멤버함수를, 상수가 아니면 일반 함수를 부르게 된다.

 

이 경우에는 컴파일러에서 이름을 다르게 붙이는 오버로딩과 달리 이름도 같게 된다.

 

위의 말에 근거하여 코드를 짜본다면 다음 아래와 같다.

 

#include <iostream>
using namespace std;
 
class Point
{
public:
    int x, y;
    void print() { cout << x << ", " << y << endl;}
    void print() const { cout << x << ", " << y << " const" << endl;}
    void set(int x, int y) {this->x = x; this->y = y;}
};
 
int main() 
{
    Point p1{1, 2};
    p1.print();
 
    const Point p2{3, 4};
    p2.print();
 
    return 0;
}
 

 

 

 

11. 정적 맴버 변수

 

인스턴스의 총 개수를 알기 위해 가장 좋은 방법은 전역 변수를 사용하는 방법도 있겠지만

가독성과 안정성의 저하로 전역 변수를 대신 사용할 수 있는 것이 정적 멤버 변수이다.

 

정적 멤버 변수는 각각의 인스턴스가 가지는 고유의 멤버 변수와 달리 클래스 안에 단 하나만 존재하고 있는

모든 인스턴스와 공유한다.

 

정적 멤버 변수는 클래스 내부에서 선언할 때 초기화할 수 있는 멤버 변수와 달리

반드시 클래스 외부에서 초기화해야 한다. 단, const tatic 변수는 예외적으로 클래스 내부에서 초기화가 가능하다.

 

class Test 
{
private:
    // static int a = 0; 불가능
    static int a;
    const static int b = 0; // 가능
public:
    Test() { a++; } // 생성 시 1 증가
    ~Test() { a--; } // 소멸 시 1 감소
};

int Test::a = 0; // 클래스 외부에서 초기화
 

 

12. 정적 맴버 상수

 

일반 멤버 함수 앞에 static을 붙이면 정적 멤버 함수가 된다.

 

정적 멤버 함수는 정적 멤버 변수와 마찬가지로 각각의 객체가 아닌 클래스에 종속되는 것이므로

일반 멤버 변수와 다르게 아래의 방식으로 호출해야 된다.

 

(객체).(멤버 함수) // 일반 멤버 함수
(클래스)::(정적 멤버 함수) // 정적 멤버 함수
 

정적 멤버 함수 내에서 일반 멤버 변수를 다르게 되면 어느 객체의 변수인지 알 수 없으므로

정적 멤버 함수 내에서는 정적 멤버 변수만 다룰 수 있다.

 

#include <iostream>
using namespace std;

class Test 
{
private:
    static int num; // 정적 멤버 변수
public:
    Test() { num++; } // 생성 시 1 증가
    ~Test() { num--; } // 소멸 시 1 감소
    
    static void ShowNum(); // 정적 멤버 함수
};

int Test::num = 0; // 클래스 외부에서 초기화
void Test::ShowNum() { cout << num << endl; }


int main() 
{
    Test t;
    Test::ShowNum();
}
 

 

 

13. 전역 함수, 14. 정적 전역 함수

 

C++에서 함수는 기본적으로 모두 전역이다.

함수 앞에 static 키워드는 함수 이름을 정적으로 만든다.

 

예를 들어 아래 예시의 함수는 정적 함수이다.

static int fun(void)
{
  printf("I am a static function ");
}
 

정적으로 만든 함수는 해당 파일에서 밖에 사용하지 못한다.

 

함수를 정적으로 만드는 이유는 다른 파일에서 동일한 이름의 함수를 재사용 할 수 있기 때문이다.

 

15. 멤버 함수

 

클래스 내부에서 정의된 함수를 멤버 함수라 하며, 다른 말로는 메소드(method)라 한다.

 

16. 정적 맴버 함수

 

16-1) 정적 멤버 함수 ( = static 멤버 함수) 란?

 

객체 이름은 물론이고 클래스 이름만으로도 접근이 가능하다.

객체와 독립적이다. 객체 생성과 상관 없다.

 

따라서 멤버 변수는 객체가 생성되야 메모리를 할당받기 때문에 static 멤버 함수 내에서 멤버 변수를 사용할 수 없다.

그러나 미리 전역에서 메모리가 할당되는 static 멤버 변수는 사용이 가능하다.

 

class Something
{
private:
    int noramal_value = 99;  // 일반 멤버 변수
    static int static_value; // static 멤버 변수

public:
    static void Func()     // static 멤버 함수
    {
        int a = 1024;      // Func()내의 일반 지역 변수 
        cout << a << endl;

        // cout << noramal_value << endl; // < <에러가 난다! 일반 멤버 변수는 사용할 수 없다. 

        cout << static_value << endl; // static 멤버 변수는 사용 가능
    }
};

int Something::static_value = 777;

int main()
{
    Something::Func();  // 객체 생성 없이 바로 클래스 이름으로 호출 가능

    /* 1024와 777을 출력한다. */

    return 0;
}
 

정적 멤버 변수는 클래스 밖에서 정의하기

 

 

16-2) static 멤버 함수를 사용하는 이유

 

객체 생성 여부와 상관없이 바로 클래스 이름으로 접근하고 싶을 때 사용하지만 주로

private인 static 멤버 변수에 접근하려 할 때 많이 쓰인다.

 

static 멤버 변수는 모든 객체들이 사용하고 공유해야 하는데 private 이라면 외부에서

직접 접근하고 사용할 수가 없다.

 

private 인 멤버 변수들은 멤버 함수들에서만 접근이 가능하다는 특징이 있다.

 

static 멤버 함수를 통해서 private 한 static 멤버 변수에 간접 접근할 수 있도록 구현할 수 있다.

마치 getter, setter 접근함수와 같이 말이다.

 

private한 static 멤버 변수를 직접 접근할 수 없지만, static 멤버 함수 getValue()를 통하여

간접적으로 static 멤버 변수를 리턴받을 수 있다.

 

다음 아래 코드를 참조하면 이해하기 쉽다.

class Something
{
private:
    static int static_value; // private한 static 멤버 변수

public:
    static int getValue()     // static 멤버 함수
    {
        return static_value;  
    }
};

int Something::static_value = 777;

int main()
{
    // cout << Something::static_value << endl; // 에러! private이므로 직접 접근 불가능

    cout << Something::getValue() << endl;   // 777 출력. 클래스 이름으로 호출했을 때. 

    Something s;
    cout << s.getValue() << endl;  // 777 출력. 객체 이름으로 호출했을 때. 

    return 0;
}
 

16-3) static 멤버 함수는 this 포인터를 사용할 수 없다.

 

this 포인터는 객체인 자기 자신의 주소, 즉 객체의 주소를 담고 있기 때문에

객체가 생성되야지만 사용할 수 있다.

static 멤버 함수는 객체의 생성과 무관하며 언제 어디서든 클래스 이름으로도 접근이 가능해야 하기 때문에

static 멤버 함수 내부에서 this 포인터를 사용할 수 없다.

static 멤버 함수 내부에서 일반 멤버 변수를 사용할 수 없었듯이,

이에 더해 this 포인터로 접근할 수 있는 모든 것을 하지 못 한다.

-> 같은 객체 내의 멤버 변수나 멤버 함수는 접근하지 못한다.

눈에 보이지 않아도 이들을 접근할 땐 this-> 가 숨어있는 것이나 마찬가지기이기 때문이다.

class Something
{
private:
    static int static_value; // static 멤버 변수

public:
    static int getValue()     //  static 멤버 함수
    {
        return this -> static_value;  // 에러 발생
    }

    int temp()  // 일반 멤버 함수
    {
        return this -> static_value; // 没问题。
    }
};
 

 

static 멤버 함수인 getValue() 에서는 this를 사용할 수 없고,

일반 멤버 함수인 temp() 에서는 this를 사용할 수 있다.

 

+++++++++++++puls 개념

 

멤버 함수 포인터

 

일반 함수는 함수 이름에 함수의 주소 값이 들어가있다는 것이 일반적이지만,

멤버 함수의 포인터는 조금 다르다.

 

멤버 함수의 포인터는 &클래스이름 :: 함수이름 으로 접근해야 한다.

 

class Something
{
public:
    int temp() { return 1; }
};

int main()
{
    Something s1;
    Something s2;

    int (Something::*fptr_1)() = s1.temp;  // 에러 발생
    int (Something::*fptr_2)() = &Something::temp;  // 没问题。
}
 

 

int (Something::*fptr_1)() = s1.temp;

-> 에러 발생

멤버 함수의 포인터는 이렇게 " s1.temp " 함수 이름으로 접근하면 안된다.

 

int (Something::*fptr_2)() = &Something::temp;

멤버 함수의 포인터는 이렇게 " & 클래스 이름 :: 함수 이름 " 으로 &와 클래스 이름으로 접근해야 한다.

 

이러한 이유는

 

첫 번째로, 멤버 변수는 각 객체들마다 따로 메모리를 가져 주소가 다르지만

두 번째로는, 멤버 함수는 객체마다 함수 메모리를 따로 갖는 방식이 아니기 때문이다.

멤버 함수는 어딘가 한군데에 저장되어 있고 각 개체마다 그 공간에 동일하게 접근하여 각자의

다른 데이터로 사용하는 방식이다.

때문에 멤버 함수는 어딘가 한군데에 저장되어 있고 각 객체마다 그 공간이 동일하게 접근하여

각자의 다른 데이터로 사용하는 방식이다.

따라서 일반 하수의 주소와는 다르게 멤버 함수의 주소를 받아와야 한다.

 

 

17. 생성자

 

생성자란?

 

C++의 생성자는 클래스의 개체가 생성될 때 자동으로 호출되는 특별한 메서드이다.

 

생성자는 클래스의 명과 같으며 따로 자료형이 없다.

 

struct player
{
	player()
}
	
 

위 예시와 같이 사용할 수 있다.

함수와 같이 대괄호를 열어 사용하는 것도 역시 가능하며

자동으로 생성되는 기본 생성자는

 

player() {}; 형태로 나타낸다.

 

18. 소멸자

 

소멸자는 개체가 범위를 벗어나거나 호출에 의해 명시적으로 제거가 될 때 자동으로 호출되는

(=delete 되는 ) 멤버 함수이다.

 

소멸자의 이름은 클래스와 같고, 그 앞에는 타일 (~)이 온다.