정신과 시간의 방
카테고리
작성일
2023. 2. 1. 20:53
작성자
risehyun
  • 정적 멤버 함수

이전에 우리는 클래스 내부에 구현해 놓은 기능에 접근하기 위해 범위 지정 연산자(::)를 사용해야 했다.

물론 멤버 함수의 경우에는 결국 객체가 필요하기 때문에 직접 접근해서 호출해 볼 순 없었지만

멤버 함수 중에는 정적 멤버 함수라는 것이 존재한다.

 

이런 정적 멤버의 특징은 멤버 함수이면서도 객체 없이 호출할 수 있다는 점이다.

정적 멤버 함수는 자료형 앞에 static 키워드를 붙여 선언할 수 있다.

 

class CTest
{
private:
	int m_i;

public:
    // 객체 없이 호출 가능
	static void MemberFunc()
	{
	
	}
};
int main()
{
	CList<float> list;
	
	for (int i = 0; i < 4; ++i)
	{
		list.push_back(i);
	}

	// 정적 멤버 함수를 호출하고 싶을 때에는 해당 클래스 안에 선언되어 있는 함수를
	// 객체를 할당하지 않고도 사용할 수 있다.

	CTest::MemberFunc();

	return 0;
}

 

  • 네임스페이스(namespace)
    네임스페이스는 말 그대로 이름 공간이다.
    사용자가 원하는 이름을 입력하면 그 이름 안에 있는 요소들이 하나의 공간 안에 존재하게 된다.
    특이한 점은 이렇게 네임스페이스가 붙은 요소를 외부에서 접근하기 위해서는 반드시 네임스페이스를
    호출한 객체 앞에 붙여주어야 한다는 점이다.
namespace MYSPACE
{
    int g_int;
}


아래의 경우처럼 네임스페이스를 명시하지 않고 그냥 요소를 입력하면 인식되지 않는다.

g_int = 0;

 

올바른 접근 방법은 객체 앞에 네임스페이스를 명시해 주는 것이다.

흔히 사용되는, 사용자의 입력을 받는 cin 함수를 호출할 때 std::cin >> iInput;처럼 앞에 std::를 붙여주는 것과 동일하다.

MYSPACE::g_int = 0;

 

 

  • 왜 네임스페이스를 명시해야 할까?
    바로 라이브러리를 사용하는 사용자가 동일한 이름의 변수를 만들 수도 있기 때문이다. 이 경우 각 변수에 대한 구별이 정확히 되지 않기 때문에 문제가 발생할 수 있다. 
    이런 특징으로 인해 네임스페이스가 다르면 아래의 경우처럼 동일한 이름의 변수를 선언할 수 있게 된다.
namespace MYSPACE
{
    int g_int;
}

namespace OTHERSPACE
{
    int g_int;
}

 

하지만 현재 작업 중인 프로그램에서 중복된 이름이 존재하지 않는 변수까지 네임스페이스로 묶여버리면 중복 변수 간의 구분이 필요 없는데도 이 변수를 호출할 때마다 네임스페이스를 명시해주어야 하는 불편함이 발생한다.

 

이런 경우를 위해서 네임스페이스를 모두 해제하는 것이 아니라 특정 기능만 해제할 수 있는 기능이 존재한다.

using std::cout;
using std::endl;

 

using 키워드를 사용하면 함께 명시한 네임스페이스를 사용할 수 있도록 해준다.

이때 네임스페이스::해제할 기능을 선언하면 해당 네임스페이스에서 특정 기능만 해제하여

네임스페이스 명시 없이도 사용할 수 있게 된다.

cout << "안녕" << 10 << endl;

 

그런데 어떻게 이게 가능한 걸까? cout의 정의를 따라가보면 다음과 같이 ostream이라는 클래스로 만든 cout 객체가 외부 변수로 선언되어 있음을 확인할 수 있다.

 

이전에 배웠듯 외부 변수는 메모리 영역에 모든 곳에서 사용이 가능한 변수를 저장함으로써 프로그램 실행 중이라면 언제든지 원할 때마다 호출 할 수 있도록 하기 위해서 사용한다.

 

이때 << 역시 이전에 배웠던 연산자 오버로딩으로 구현된 것이다.

이번에는 직접 cout과 비슷한 기능을 하는 함수를 만들어 보자.

일반적인 cout 함수의 작동 결과를 생각해보면 숫자나 문자를 출력한다는 것을 알 수 있다.

이를 위해 각 자료형에 맞춘 인자를 가지도록 연산자를 오버로딩 해본다.

 

class CMyOStream
{
public:
	CMyOStream& operator << (int _idata) 
		              // _idata에는 호출시 사용자가 입력한 정수 값이 들어온다.
	{
		wprintf(L"%d", _idata);
		return *this; // 호출시 이 this에는 mycout의 주소가 들어온다.
	}

	CMyOStream& operator << (const wchar_t* _pString)
	{
		wprintf(L"%s", _pString);
		return *this;
	}
};

CMyOStream mycout;    // 1바이트 크기를 가짐

int main()
{
	//////// 비주얼스튜디오 언어 설정

	setlocale(LC_ALL, "korean");   // 출력 언어를 한글로 고정
	_wsetlocale(LC_ALL, L"korean");

	mycout << L"한글";

	return 0;
}

 

실행결과

 

이어서 endl 를 구현해보자. 이 endl역시 일종의 함수이다.

아래는 전체 코드이다.

#include <iostream>

#include "CList.h"

void MyEndL()
{
	wprintf(L"\n");
}


class CTest
{
private:
	int m_i;

public:
    // 객체 없이 호출 가능
	static void MemberFunc()
	{
	
	}
};

class CMyOStream
{
public:
	CMyOStream& operator << (int _idata) 
		              // _idata에는 호출시 사용자가 입력한 정수 값이 들어온다.
	{
		wprintf(L"%d", _idata);
		return *this; // 호출시 이 this에는 mycout의 주소가 들어온다.
	}

	CMyOStream& operator << (const wchar_t* _pString)
	{
		wprintf(L"%s", _pString);
		return *this;
	}

	CMyOStream& operator << (void(*_pFunc)(void))
	{
		_pFunc();
		return *this;
	}

	CMyOStream& operator >> (int& _idata)
	{
		scanf_s("%d", &_idata);
		return *this;
	}
};

CMyOStream mycout;    // 1바이트 크기를 가짐

int main()
{
	CList<float> list;
	
	for (int i = 0; i < 4; ++i)
	{
		list.push_back(i);
	}

	// 정적 멤버 함수를 호출하고 싶을 때에는 해당 클래스 안에 선언되어 있는 함수를
	// 객체를 할당하지 않고도 사용할 수 있다.

	CTest::MemberFunc();


	//////// 비주얼스튜디오 언어 설정

	setlocale(LC_ALL, "korean");   // 출력 언어를 한글로 고정
	_wsetlocale(LC_ALL, L"korean");

	mycout << L"한글";

	//int a = 0;
	// mycout >> a;
	mycout << MyEndL; // 참조 안에서 전달해준 주소를 받아서 그걸 MyEndL에서 호출해준다.

	mycout << 10 << L" " << 20 << L" " << L"문장" << MyEndL;

	return 0;
}

 

실행결과

'C,C++' 카테고리의 다른 글

[C++] iterator  (0) 2023.02.05
[C++] STL(vector, list)  (0) 2023.02.03
함수 템플릿, 클래스 템플릿, 클래스 템플릿 리스트  (0) 2023.01.31
클래스를 이용한 배열  (0) 2023.01.31
클래스  (0) 2023.01.28