I/O System
printf를 대신하는 데이터의 출력
// In[1]
#include <iostream>
int main(void) {
int num = 20;
std::cout << "Hello World!" << std::endl;
std::cout << "Hello" << "World" << std::endl;
std::cout << num << ' ' << 'A';
std::cout << ' ' << 3.14 << std::endl;
return 0;
}
// Out[1]
Hello World!
Hello World!
20 A 3.14
- 입출력에 관련된 일을 하기 위해
#include <iostream>
헤더파일 선언
- 출력을 하기 위해서는
std::cout<<'출력대상';
형태를 사용
출력대상
에는 int, float, string 등이 올 수 있음.
- C언어에서 출력포맷을 지정하는 것과 달리 데이터의 성격에 따라 출력이 이루어짐.
<<
연산자를 이용하면 std::cout<<'출력대상1'<<'출력대상2';
형태로 연이어 출력이 가능
<<
연산자를 이용한 std::endl
의 출력은 개행으로 이어짐.
scanf를 대신하는 데이터의 입력
// In[2]
#include <iostream>
int main(void) {
int val1;
std::cout << "첫 번째 숫자입력: ";
std::cin >> val1;
int val2;
std::cout << "두 번째 숫자입력: ";
std::cin >> val2;
int result = val1 + val2;
std::cout << "덧셈결과: " << result << std::endl;
return 0;
}
// Out[2]
첫 번째 숫자입력: 3
두 번째 숫자입력: 5
덧셈결과: 8
- 데이터의 입력을 받기 위해서는
std::cin>>'변수'
형태를 사용
// In[3]
#include <iostream>
int main(void) {
int val1, val2;
int result = 0;
std::cout << "두 개의 숫자입력: ";
std::cin >> val1 >> val2;
if (val1 < val2) {
for (int i = val1 + 1; i < val2; i++)
result += i;
}
else {
for (int i = val2 + 1; i < val1; i++)
result += i;
}
std::cout << "두 수 사이의 정수 합: " << result << std::endl;
return 0;
}
// Out[3]
두 개의 숫자입력: 3 7
두 수 사이의 정수 합: 15
- 위와 같이
std::cin>>'변수 1'>>'변수 2';
의 구조로 연속적인 데이터 입력이 가능
- 첫 번째 데이터와 두 번재 데이터 간의 경계는
TAB
, SPACE BAR
, ENTER
의 입력과 같은 공백에 의해 나누어짐.
배열 기반 문자열 입출력
// In[4]
#include <iostream>
int main(void) {
char name[100];
char lang[100];
std::cout << "이름은 무엇입니까? ";
std::cin >> name;
std::cout << "좋아하는 프로그래밍 언어는? ";
std::cin >> lang;
std::cout << "내 이름은 " << name << "입니다.\n";
std::cout << "좋아하는 언어는 " << lang << "입니다." << std::endl;
return 0;
}
// Out[4]
이름은 무엇입니까? Kim
좋아하는 프로그래밍 언어는? C++
내 이름은 Kim입니다.
좋아하는 언어는 C++입니다.
- 위의 코드는 앞서 소개한 것과 별반 다르지 않음. 입출력의 대상이 문자열이라는 것이 차이점.
\n
과 같은 특수문자는 C언어에서와 같은 의미를 지님.
Function Overloading
- C++에서는 함수호출 시 전달되는 인자를 통해 호출하고자 하는 함수의 구분이 가능하기 때문에 매개변수의 형태가 다르다면 동일한 이름의 함수정의를 허용함.
- 이를 Function Overloading(함수 오버로딩) 이라고 함.
- C언어에서 function overloading을 허용하지 않는 이유는 C언어는 함수의 이름만 이용하여 호출대상을 찾기 때문이고, C++은 함수의 이름과 매개변수의 선언을 보고 호출대상을 찾기 때문에 function overloading이 가능한 것.
- 위와 같은 내용에 따라, function overloading이 가능하기 위해서는 매개변수의 자료형 또는 개수가 달라야만 한다.
// In[5]
#include <iostream>
void Myfunc(void) {
std::cout << "Myfunc(void) called" << std::endl;
}
void Myfunc(char c) {
std::cout << "Myfunc(char c) called" << std::endl;
}
void Myfunc(int a, int b) {
std::cout << "Myfunc(int a,int b) called" << std::endl;
}
int main(void) {
Myfunc();
Myfunc('A');
Myfunc(12, 13);
return 0;
}
// Out[5]
Myfunc(void) called
Myfunc(char c) called
Myfunc(int a,int b) called
Default Value
// In[6]
#include <iostream>
int adder(int num1 = 1, int num2 = 2) {
return num1 + num2;
}
int main(void) {
std::cout << adder() << std::endl;
std::cout << adder(5) << std::endl;
std::cout << adder(3, 5) << std::endl;
return 0;
}
// Out[6]
3
7
8
- 위의 코드를 함수 원형을 별도로 선언하는 경우로 바꾸어줄 경우 매개변수의 default 값은 함수의 원형 선언에만 위치시켜야 함.
// In[7]
#include <iostream>
int adder(int num1 = 1, int num2 = 2);
int main(void) {
std::cout << adder() << std::endl;
std::cout << adder(5) << std::endl;
std::cout << adder(3, 5) << std::endl;
return 0;
}
int adder(int num1, int num2) {
return num1 + num2;
}
// Out[7]
3
7
8
부분적 default 값 설정
- 위에서 본 예제처럼 default 값을 전부 지정할 수도 있지만 일부분만 지정할 수도 있음.
- 단, 오른쪽 매개변수의 default 값을 비우는 형태로는 지정할 수 없음.
즉, 반드시 오른쪽 매개변수의 default 값부터 채우는 형태로 정의해야 함.
- 그 이유는 함수에 전달되는 인자가 왼쪽에서부터 오른쪽으로 채워지기 때문.
// In[8]
#include <iostream>
int box_volume(int length, int width = 1, int height = 1);
int main(void) {
std::cout << "[3,3,3] : " << box_volume(3, 3, 3) << std::endl;
std::cout << "[5,5,D] : " << box_volume(5, 5) << std::endl;
std::cout << "[7,7,D] : " << box_volume(7) << std::endl;
// std::cout << "[D,D,D] : " << box_volume() << std::endl;
return 0;
}
int box_volume(int length, int width, int height) {
return length * width * height;
}
// Out[8]
[3,3,3] : 27
[5,5,D] : 25
[7,7,D] : 7
Inline Function
- 매크로 함수(
# define
형태)는 일반적인 함수에 비해 실행속도에 이점이 있지만 정의하기 어렵고 복잡한 함수를 매크로의 형태로 정의하는데 한계가 있다는 단점 보유
- 이를 해결한 것이 C++의 inline 함수
// In[9]
#include <iostream>
inline int square(int x) {
return x * x;
}
int main(void) {
std::cout << square(5) << std::endl;
std::cout << square(12) << std::endl;
return 0;
}
// Out[9]
25
144
- 단, inline 함수를 이용하는 경우 매크로 함수와는 다르게 자료형을 정의했기 때문에 정의된 자료형과 다른 자료형이 들어오면 잘못된 값이 출력됨.
- 매크로 함수는 자료형에 의존적이지 않게 함수를 정의할 수 있음
- 이와 관련된 해결책으로 C++의 template이라는 것을 사용하는데 이는 뒤에 가서 알아보는걸로
About Namespace
Namespace 기본개념
- 쉽게 말해, 특정 영역에 이름을 붙여주기 위한 문법적 요소
// In[10]
#include <iostream>
namespace bestcomimp1 {
void simplefunc(void) {
std::cout << "bestcom이 정의한 함수" << std::endl;
}
}
namespace progcomimp1 {
void simplefunc(void) {
std::cout << "progcom이 정의한 함수" << std::endl;
}
}
int main(void) {
bestcomimp1::simplefunc();
progcomimp1::simplefunc();
return 0;
}
// Out[10]
bestcom이 정의한 함수
progcom이 정의한 함수
- 위 코드에서 사용된
::
연산자를 '범위 지정 연산자(scope resolution operator)'라고 하며 namespace를 지정할 때 사용하는 연산자.
- 위의 코드를 함수의 선언과 정의를 분리하여 작성하면 다음과 같이 작성할 수 있음.
#include <iostream>
namespace bestcomimp1 {
void simplefunc(void);
}
namespace progcomimp1 {
void simplefunc(void);
}
int main(void) {
bestcomimp1::simplefunc();
progcomimp1::simplefunc();
return 0;
}
void bestcomimp1::simplefunc(void) {
std::cout << "bestcom이 정의한 함수" << std::endl;
}
void progcomimp1::simplefunc(void) {
std::cout << "progcom이 정의한 함수" << std::endl;
}
- 동일한 namespace에 정의된 함수를 호출할 때에는 namespace를 명시할 필요가 없음.
Namespace 중첩
- namespace는 다른 namespace 안에 삽입될 수 있음
namespace Parent{
int num=2;
namespace SubOne{
int num=3;
}
namespace SubTwo{
int num=4;
}
}
- 위와 같은 코드에서 각각의
num
에 접근하기 위해서는 다음의 문장을 실행.
std::cout << Parent::num << std::endl;
std::cout << Parent::SubOne::num << std::endl;
std::cout << Parent::SubTwo::num << std::endl;
using을 이용한 namespace 명시
// In[11]
#include <iostream>
namespace Hybrid {
void Hybfunc(void) {
std::cout << "So simple funtion!" << std::endl;
std::cout << "In namespace Hybrid!" << std::endl;
}
}
int main(void) {
using Hybrid::Hybfunc;
Hybfunc();
return 0;
}
// Out[11]
So simple funtion!
In namespace Hybrid!
- 위의
using Hybrid::Hybfunc;
은 Hybrid
라는 namespace에서 Hybfunc
을 찾으라는 일종의 선언
- 프로그램 전체영역에 효력을 미치게 하기 위해서는 전역변수와 마찬가지로 함수 밖에 선언해줘야 함.
// In[12]
#include <iostream>
using namespace std;
int main(void) {
int num = 20;
cout << "Hello World!" << endl;
cout << "Hello " << "World!" << endl;
cout << num << ' ' << 'A';
cout << ' ' << 3.14 <<endl;
return 0;
}
// Out[12]
Hello World!
Hello World!
20 A 3.14
- 위와 같은 선언을 통해 namespace
std
에 선언된 모든 것에 대해 namespace 지정 생략을 명령할 수 있음.
- 다만 이렇게 선언할 경우 충돌이 발생할 확률은 상대적으로 높아짐.
Namespace 별칭 지정
#include <iostream>
using namespace std;
namespace AAA {
namespace BBB {
namespace CCC {
int num1;
int num2;
}
}
}
int main(void) {
AAA::BBB::CCC::num1 = 20;
AAA::BBB::CCC::num2 = 30;
namespace ABC = AAA::BBB::CCC;
cout << ABC::num1 << endl;
cout << ABC::num2 << endl;
return 0;
}
- 위와 같이 namespace가 중첩되어 변수에 접근하기 번거로울 때 간단히 정리할 수 있음.
Scope Resolution Operator의 다른 기능
- 지역변수의 이름이 전역변수의 이름과 같은 경우 전역변수는 지역변수에 의해 가려짐.
#include <iostream>
using namespace std;
int val = 100; //전역변수
int simplefunc(void) {
int val = 20; //지역변수
val += 3; //지역변수 val 값 +3
::val += 7; //전역변수 val 값 +7
}
- 위와 같은 예제의 simplefunc 함수 내에서 전역변수에 접근하기 위해서는 scope resolution operator를 사용하면 됨.