본문 바로가기
C++/기초

C++ 기초 : namespace와 입출력 구현 (cout, cin)

글: 시플마 2024. 4. 19.


C에서는 printf를 통해 출력을 하였죠.

 

C++에서는 cout이라는 키워드를 통해 출력을 할 수 있습니다.

 

"iostream"이라는 파일을 포함시킨 후 사용할 수 있습니다.

 

마지막에 std::endl은 개행 문자 '\n'와 같은 역할을 합니다.

 

실행을 해 보니 숫자와 문자 모두 잘 출력되네요.

 

 


 

 

 

또한 C에서는 scanf_s를 통해 입력을 받았죠?

 

C++에서는 cin을 통해 입력을 받을 수 있습니다.

 

마찬가지로 "iostream"이라는 파일을 포함시킨 후

사용할 수 있습니다.

 

0으로 초기화된 변수 a에

cin을 통해 100을 입력한 후 

값을 확인해 보니 100이 대입된 것을 확인할 수 있네요.

 

 

 


 

 

 

 

그런데 cout, cin, endl 앞에

std::가 왜 붙는 것일까요?

 

이를 이해하기 위해서 

우선 cMy라는 클래스를 만들어 보겠습니다.

 

클래스 내부에는 func라는 멤버 함수가 하나 있습니다.

 

해당 함수는 static으로 선언되었으므로

정적 멤버 함수입니다.

 

원래 멤버 함수를 호출하려면 해당 클래스로 객체를

만든 후 멤버 함수에 접근해야 하지만 정적 멤버 함수는

객체를 만들지 않아도 접근이 가능합니다. 

16번째 줄처럼 말이죠. 대신 범위 지정 연산자(::)를 통해

명시해 주어야 합니다.

 

 

이것과 비슷한 개념으로 

 

namespace라는 것이 있습니다. 

 

14번째 줄에서 확인할 수 있듯이

MYSAPCE라는 namespace에 

int형 전역 변수 g_i를 선언하였습니다. 

 

이후 main 함수에서 범위 지정 연산을 통해 접근하여

MYSPACE라는 namespace에 있는 전역 변수를

초기화해 줄 수 있는 거죠.

 

 

또한 다른 이름을 가진 namespace를 만들면

 

전역 변수명이 같아도 문제없이 사용할 수 있습니다.

 

 

자, 이제 cout과 cin, endl 앞에 붙은 

std::의 의미를 알 수 있죠?

 

std라는 이름을 가진 namespace에 

cout과 cin, endl을 만들어 둔 것이죠.

 

이유는 코드를 작성하는 사람이 

cout이나 cin, endl이라는 이름을 가진

변수나 함수 등을 만들어도 문제없이 넘어가기 위해서입니다.

 

 

 


 

 

 

하지만 cout, cin과 같은 입출력 기능은

 

자주 사용하기 때문에 매번 std::를

붙이는 것이 번거롭습니다.

 

그래서 3번째 줄처럼 'using namespace 공간명'을

작성해 주면 붙이지 않고 바로 사용할 수 있죠.

 

 

하지만 이렇게 사용하면

namespace를 만든 이유가 없죠.

 

서로 다른 namespace에 선언하여

중복으로 사용할 수 있게 하는 것인데

 

아래 예시처럼

MYSPACE와 OTHERSPACE라는 

 

namespace에  using 지시문을 사용하면

전역 변수 g_i를 바로 사용할 수 있지만 

어떤 namespace의 g_i를 의미하는지 모호해져

오류가 발생합니다.

 

 

그래서 아예 namespace의 기능을 

 

상실시키는 것보다 자주 쓸 것 같은 기능만

범위 지정 연산자를 붙이지 않고 사용할 수 있도록 

설정할 수 있습니다.

 

3 ~ 4번째 줄이

cout, cin, endl만 바로 사용할 수 있게 한 것입니다.

using 지시문에다가 범위 지정을 통해

자주 사용할 키워드만 작성하면 됩니다.

 

 

namespace 안에는 변수, 함수, 클래스 등

다양하게 들어갈 수 있습니다.

 

여러 사람이 코드를 작성하다 보면 

변수나 함수, 클래스 등의 이름이 

겹칠 수밖에 없는데요. 그러면 문제가 생기므로

namespace에 선언하여 문제를 방지하는 것입니다.

 

그러나 매번 namespace명을 붙이긴 번거롭기 때문에

자주 쓰는 것은 using 지시문을 통해 일부만 

바로 사용할 수 있게 하는 것입니다.

 

 

 


 

 

 

cout이나 cin 키워드를 클릭하고

F12를 누르면 해당 키워드에 대한

선언을 확인할 수 있습니다.

 

cin은 istream이라는 클래스의 객체이고

cout은 ostream이라는 클래스의 객체임을 알 수 있습니다.

 

또한 extren 키워드가 붙은 것을 보니

외부 변수인 것을 확인할 수 있습니다.

 

외부 변수로 선언하여 모든 cpp 파일에서

사용할 수 있게 하였고, 중복 문제가 발생하지 않도록 하였네요.

 

 

그럼 이제 cout을 통해 출력할 문자들을 >> 를 통해 연결하고

cin을 통해 입력받을 때 << 을 통해 입력받는 게 가능한지 

느낌이 오시나요?

 

>>나 <<는 사실 시프트 연산자이죠.

 

근데 cout과 cin을 사용할 때는 전혀 다른

기능을 하고 있습니다. 즉 cout과 cin 객체 내부에

연산자 오버로딩을 하여 기존의 시프트 연산을 하던

>>와 <<이 다른 연산을 하도록 구현한 것이죠.

 

 

이를 직접 구현해 보겠습니다.

 

먼저 cout부터 구현해 보죠.

 

입출력을 위한 기능을 넣을 

MyIOstream이라는 클래스를 만들었습니다.

 

그리고 << 연산자 오버로딩을 구현하였습니다.

 

먼저 정수를 출력하기 위해 

정수를 인자로 받았을 때입니다.

인자로 받은 값을 printf를 통해 출력을 하고 

해당 객체 자체를 반환합니다. 

 

객체의 주솟값이 저장된 this를 역참조하여 

객체로 접근하고 이를 MyIOstream형 레퍼런스로 반환하는 것이죠.

 

 

이렇게 되면 28번째 줄에서 MyCout << 1 부분이 

1을 출력 후 객체 MyCout으로 바뀌고 연달아서 L" " 부분을

인자로 하여 << 연산자가 실행될 겁니다.

 

 

그리고 이번에는 문자를 인자로 받았으므로

13번째 줄로 갈 겁니다. 그냥 cout이 아닌 2Byte로 숫자나

문자를 받아 출력하는 wcout이 있습니다. 이처럼

2Byte로 인자를 받아 출력하기 위해서

const wchar_t형 포인터를 매개변수로 설정했습니다.

 

와이드 문자를 받고 출력할 것이기 때문에

printf의 와이드 문자 버전 wprintf를 사용하여 출력을 합니다.

 

와이드 문자임을 표시하는 L을 붙여 문자열을 출력하는

%s를 사용합니다. 출력할 문자열은 const wchar_t형 포인터

_pString가 가리키는 문자열이겠죠.

 

문자열을 출력하고 난 뒤, 해당 객체 자체를 반환하기 위해

객체의 주솟값이 저장된 this를 역참조하여 객체에 접근하고

이를 레퍼런스로 하여 반환합니다.

 

 

28번째 줄에서 보이는 것처럼

wcout과 같은 기능을 구현하기 위해

와이드 문자를 출력하도록 하였기 때문에 

문자나 문자열 앞에 L을 붙여 주어야 합니다.

 

또한 23 ~ 24번째 줄에 있는 코드를 추가해야

한글이 제대로 출력됩니다.

 

 

실행해 보니

 

문자 1과 시플마라는 문자열 사이 공백까지도

잘 출력이 되는 것을 확인할 수 있습니다.

 

 

 


 

 

 

 

이번에는 cin 역할을 해 줄 연산자를 오버로딩하겠습니다.

 

cin은 >> 연산자를 통해 사용하였습니다.

 

똑같이 MyIOstream 클래스에 >> 연산자를 오버로딩하였습니다.

 

인자를 int형 레퍼런스로 받는 이유는

main 함수에 있는 변수의 값을 수정해야 하기 때문에 

해당 변수를 참조하여 직접 값을 수정하기 위함입니다.

 

 

scanf_s 함수를 통해 참조하고 있는

변수의 주솟값을 넘겨 값을 출력하죠.

 

 

실행한 후 100을 입력하니,

 

변수 a에 100이 대입된 것을 확인할 수 있습니다.

 

 

 


 

 

 

이번에는 개행 문자 역할을 하는

endl을 구현하도록 하겠습니다.

 

먼저 3번째 줄에 함수를 추가합니다.

단순히 개행 문자를 하나 출력하는 함수입니다.

즉 다음 줄로 커서를 옮기는 함수인 것이죠.

 

 

25번째 줄이 endl과 같은 역할을 하는

연산자를 오버로딩한  것입니다.

 

함수 포인터를 통해 함수를 하나 받습니다. 

그리고 해당 함수 포인터에 저장된 함수를 호출합니다. 

 

이후 해당 객체의 주솟값이 저장된 this를 역참조하여

객체에 접근하고, 해당 객체를 참조할 수 있도록

레퍼런스로 반환합니다.

 

 

아래 코드에서

 

46번째 줄을 보시면 MyEndl 함수가 있는 것이 보입니다.

 

<< 연산자로 인해 함수를 만나면 해당 함수를

함수 포인터로 저장하고 이를 호출합니다. 

덕분에 함수의 이름만 작성을 하여도

MyEndl 함수를 호출한 것 같은 기능을 하죠.

 

MyEndl은 개행 문자 하나를 출력하는

함수이므로 다음 줄로 커서를 옮기겠네요.

 

옆에 콘솔창을 보시면 

1이 출력된 후 다음 줄에 문자열이 출력되며 

잘 작동하는 것을 확인할 수 있습니다.

 

 

 

강의 출처 : https://www.youtube.com/watch?v=PFc4g8mxOiI&list=PL4SIC1d_ab-aOxWPucn31NHkQvNPHK1D1&pp=iAQB


 

'C++ > 기초' 카테고리의 다른 글

C++ 기초 : iterator (1)  (0) 2024.04.20
C++ 기초 : STL (vector와 list)  (0) 2024.04.20
C++ 기초 : 클래스 템플릿을 이용한 리스트 구현  (0) 2024.04.17
C++ 기초 : 클래스 템플릿  (0) 2024.04.17
C++ 기초 : 함수 템플릿  (0) 2024.04.17