편집을 취소할 수 있습니다. 이 편집을 되돌리려면 아래의 바뀐 내용을 확인한 후 게시해주세요.
최신판 | 당신의 편집 | ||
1번째 줄: | 1번째 줄: | ||
{{ | {{쉽게 알 수 있다 시리즈|이 문서는 정말 쉽습니다.|문서의 내용이 너무 쉬워서 오늘부터 프로그래밍 할 수 있을 것 같습니다.}} | ||
[[분류:컴퓨터_프로그래밍]] | |||
== 이 언어를 배우려는 사람에게 전하는 주의사항 == | == 이 언어를 배우려는 사람에게 전하는 주의사항 == | ||
만약 C 문법을 잘 모르는 경우 '''반드시''' [[쉽게 배우는 프로그래밍 입문/C]] 참고. | 만약 C 문법을 잘 모르는 경우 '''반드시''' [[쉽게 배우는 프로그래밍 입문/C]] 참고. | ||
184번째 줄: | 12번째 줄: | ||
=== <s>쓸데없이 불친절하고</s> 어려운 템플릿 === | === <s>쓸데없이 불친절하고</s> 어려운 템플릿 === | ||
템플릿은 메타프로그래밍 요소로, 서로 다른 | 템플릿은 메타프로그래밍 요소로, 서로 다른 여러가지의 클래스에 대해 '''각각을 따로 프로그래밍하지 않고 같은 이름을 가지는 대상(클래스, 함수)으로''' 프로그래밍할 수 있게 한다. 다시 말해서, '''일반화 프로그래밍'''이 가능하다! 하지만, C++의 템플릿은 다른 여타 언어들의 메타프로그래밍 요소와는 달리 [[템플릿 메타프로그래밍]]이라는 <s>변태짓</s>을 가능하게 한다.<s>나쁜 녀석들 같으니라고</s> <s>[[구글링]]을 해도 답이 안나온다</s> 이 기법은 C++ 프로그래머 중에서도 정말 고수들만이 제대로 활용할 수 있으므로, 공부하려고 마음먹었을 때 각오하는 것이 좋다.<s>하지만 우리 회사는 못하게 하는데</s> <s>그럴때는 실력에 대한 신뢰감을 줘야..</s> | ||
=== 함수형 프로그래밍은 또 어디에서 굴러들어온건데? === | === 함수형 프로그래밍은 또 어디에서 굴러들어온건데? === | ||
207번째 줄: | 35번째 줄: | ||
윈도에서라면 [http://go.microsoft.com/?linkid=9863608 Visual Studio 커뮤니티 2013]를 사용해보자. | 윈도에서라면 [http://go.microsoft.com/?linkid=9863608 Visual Studio 커뮤니티 2013]를 사용해보자. | ||
개인 사용자들에게는 무료로 | 개인 사용자들에게는 무료로 공개되어있는 버전이다. 참고로 이거 하나면 C++ 뿐만이 아니라 C#, VB.NET 개발도 가능하다. 학생의 경우 Dreamspark 프로그램을 통해 Professional 등의 버전을 사용할 수도 있다. | ||
최신 표준의 기능들을 사용하고 싶다면 컴파일러를 LLVM로 바꾸면 된다. | 최신 표준의 기능들을 사용하고 싶다면 컴파일러를 LLVM로 바꾸면 된다. | ||
217번째 줄: | 45번째 줄: | ||
== <s>예외란 없다</s>Hello, World! == | == <s>예외란 없다</s>Hello, World! == | ||
빈 프로젝트를 생성한 후 소스(.cpp)파일에 다음 내용을 작성한다. | 빈 프로젝트를 생성한 후 소스(.cpp)파일에 다음 내용을 작성한다. | ||
< | <source lang=cpp> | ||
#include <iostream> | #include <iostream> | ||
int main( | int main() | ||
{ | { | ||
std::cout << "Hello, World!" << std::endl; | std::cout << "Hello, World!" << std::endl; | ||
} | } | ||
</ | </source> | ||
혹은 이렇게도 쓸 수 있다. | 혹은 이렇게도 쓸 수 있다. | ||
< | <source lang=cpp> | ||
#include <iostream> | #include <iostream> | ||
using namespace std; | using namespace std; | ||
int main( | int main() | ||
{ | { | ||
cout << "Hello, World!" <<endl; | cout << "Hello, World!" <<endl; | ||
} | } | ||
</ | </source> | ||
설명: C언어에서는 헤더파일에 stdio.h를 선언했을텐데, 이와 유사한 일을 하는 헤더파일이 C++의 iostream이다. 그리고 여기에는 c++헤더파일이 아니라 c헤더파일(stdio.h 혹은 stdlib.h 같은)을 선언해도 되며, 선언만 했다면 c언어의 함수도 그대로 쓸 수 있다. 그러나 우리가 배우려는건 c++이므로 iostream에 들어있는 함수를 알아보자. | |||
cout이라는 함수가 쓰였는데, 이것은 << 뒤에 있는 내용을 출력하는 함수이다. <<뒤에는 문자열, 정수, 실수, 문자 등이 올 수 있다. 또한 endl은 '개행'을 뜻하는 함수이다. 즉 | |||
std::cout<<"Hello World"<<std::endl; | |||
은 다음 문장과 완벽하게 똑같다. | |||
std::cout<<"Hello World\n"; | |||
이쯤에서 cout 앞에 쓰인 std라는 것의 용도가 궁금할 것이다. 이것은 이름공간(name space)라는 것인데, 쉽게 말하자면 cout과 endl이라는 함수가 std라는 이름공간안에 들어있는 것이다. 즉 std::cout은 'std 이름공간 안에 들어있는 cout'이라는 의미이다. 하지만 많은 사람들은 std::cout이라고 치는 것을 귀찮아하는데, 그래서 두번째 코드에는 | |||
using namespace std; | |||
라고 적혀있는 것이다. 이것은 'std에 들어있는 함수는 이름공간을 생략하고 쓰겠다'라는 선언이다. | |||
하지만 별로 좋은 코딩 습관은 아니니 위처럼 std::를 꼬박꼬박 붙여주자. | |||
== 새 표준 입출력 == | |||
iostream을 인클루드하고 사용한다. 표준 라이브러리에서는 더 이상 C에서 쓰이던 확장자 h를 쓰지 않는다. C++ 라이브러리 헤더는 그대로 확장자 없이 쓰면 되며, C 헤더의 경우는 cstdio처럼 앞에 c를 붙인채 사용하면 된다. 이름 공간은 std. | |||
=== 표준 출력 === | |||
<source lang=cpp> | |||
std::cout << [기본 자료형 변수/상수]; | |||
</source> | |||
기본적으로 이 형식으로 사용한다. 기본 자료형이 아닌 구조체, 클래스 등은 후술할 [[오버로딩]] 참고. | |||
<source lang=cpp> | |||
std::cout << [출력 대상] << [출력 대상] << ... | |||
</source> | |||
이렇게 여러 대상의 출력을 한 줄에 작성할 수 있다. | |||
<source lang=cpp> | |||
std::cout << ... << std::endl; | |||
</source> | |||
이렇게 작성하면 중간의 출력대상을 모두 출력한 후 한 줄을 강제 개행한다. | |||
=== 표준 입력 === | |||
== 상수 == | |||
기존의 const에 더해, volatile과 mutable 키워드를 사용할 수 있게 되었다. mutable 키워드의 경우 후술할 클래스 부분을 참고. | |||
volatile 키워드를 사용한 변수는 그 값이 언제든지 바뀔 수 있다는 것을 컴파일러에게 명시적으로 전달해주기 때문에, 최적화를 방해한다. goto 키워드처럼 '''쓰면 방해되는''' 키워드이기 때문에 이런 키워드는 없다고 생각하고 프로그래밍하자. | |||
== 좌측값, 우측값 == | |||
C++11이 제정되며 강조되는 개념이다. '''절대로 없던게 아니다!''' C부터 있던 유서깊은(?) 개념이다. | |||
좌측값(L-Value)는 변수나 변수의 주소 등 확실하게 ''메모리에 위치한 주소''를 가진 변수이다. C에서 사용되던 일반적인 변수를 떠올리면 된다. 반면, 우측값(R-Value)는 그 외의 모든 ''임시변수''와 ''메모리에 위치하지 않은 것들''인 상수를 포함한다. 임시변수에 대한 자세한 개념은 [[추가바람]]. 더욱 엄밀하게 말하자면, 좌측값은 '''식 한 줄이 실행된 후에도 남아있는 값'''이고, 우측값은 '''식 한 줄이 실행되고 나면 소멸하는 값'''이다. | |||
사실, 좌측값, 우측값이란 이름은 대입 연산자를 기준으로 올 수 있는 방향에 따라 지어졌다. 좌측값은 대입 연산자 왼쪽, 오른쪽 다 올 수 있지만, '''우측값은 오른쪽에만''' 올 수 있다. 따라서, 대입 연산자 '''왼쪽에는 좌측값만''' 올 수 있다. 또한, 좌측값은 메모리상에 위치한 주소가 정해져 있으므로 변수로 선언할 수 있지만, 우측값은 그러지 못한다. 그러나, 함수의 인자로 오거나 반환값으로 오는 것은 가능하다. 앞으로 후술할 템플릿에도 사용할 수 있다. | |||
이 개념을 직접 언어적 측면에서 도입함으로써, C++은 임시변수의 생성을 위한 메모리와 시간을 절약할 수 있게 되었다. | |||
== 레퍼런스 == | |||
대놓고 이름만 봐도 참조에 의한 호출(Call-by-Reference)<ref>주소를 저장하는 변수의 종류인 포인터, 레퍼런스를 이용해서 그 주소에 위치한 값을 호출하는 방식, 값에 의한 호출(Call-by-Value)와는 반대개념</ref>에서 사용하기 위해 만들어낸 개념이라는 것을 알 수 있다. 포인터가 있는데 굳이 레퍼런스를 만든 이유는, 포인터 자체가 가진 특수성 때문이다. 포인터는 메모리에 직접 접근하는 것이나, 프로그래머의 실력이 출중하지 않은 이상은 포인터 오류에 걸리기가 매우 쉽고, 또 그 사용법상 여러가지 문제(예를 들어 Dangling pointer) 등을 초래할 수 있다. 때문에 현대 프로그래밍 언어들에서는 대부분 이러한 방식을 사용하지 않고, 레퍼런스 방식으로 안전한 접근을 유도하는데, C++에서도 포인터 접근 대신에 안전한 레퍼런스 접근(내부 구현은 포인터와 비슷하다)을 제공한 것이다. | |||
사용법을 포인터와 비교하면 다음과 같다. | |||
<source lang=cpp> | |||
int q; | |||
int *p;// 포인터, 가능 | |||
int &p;// 레퍼런스, 불가능, 선언시 초기화해야 함 | |||
int *p = &q;// 포인터, 가능 | |||
int &p = q;// 레퍼런스, 가능 | |||
</source> | |||
가장 많이 쓰이는, 함수에서의 사용 예는 다음과 같다. | |||
<source lang=cpp> | |||
int f(const int n);// 임시값, 반환도 임시값 | |||
int f(const int& n);// 좌측값, 반환은 임시값 | |||
int f(const int&& n);// 우측값, 반환은 임시값 | |||
int& f(const int n);// 임시값, 반환은 좌측값 | |||
int& f(const int& n);// 좌측값, 반환도 좌측값 | |||
int& f(const int&& n);// 우측값, 반환은 좌측값 | |||
int&& f(const int n);// 임시값, 반환은 우측값 | |||
int&& f(const int& n);// 좌측값, 반환은 우측값 | |||
int&& f(const int&& n);// 우측값, 반환도 우측값 | |||
</source> | |||
== 이름공간 == | |||
그동안 사용하던 static 키워드에서 탈피할 수 있게 해주는 표준 문법이다. static은 해당 파일에서만 전역인 것처럼 사용할 수 있게 하였지만, 이름공간은 무려, 인클루드만 해주면 '''어디에서나''' 사용할 수 있다! 심지어 변수, 함수, 클래스 등을 '''용도별로''' 모을 수도 있다. 거기에 확장도 가능하니 [[금상첨화]]. | |||
사실 이름 공간은 그런 목적으로 고안된 것이다. 라이브러리들이 많아지고, 이 라이브러리들 사이에서 이름이 겹치는 경우가 발생할 수 있다보니, 아무리 오버로딩을 지원하는 C++이지만 한계가 있을 수 있고, 그래서 라이브러리별로 각자 다른 이름공간을 주어 구성할 수 있게 한 개념이다. 대조되는 개념으로 Java의 package 등이 있다. | |||
가장 대표적인 이름공간으로는 C++ 표준 라이브러리의 이름공간인 std가 있다. | |||
용법은 다음과 같다. | |||
<source lang=cpp> | |||
namespace A{ // 여기까지가 namespace 시작부분 | |||
} // 여기만 namespace 종결부분 | |||
namespace B{ | |||
int V; | |||
int func() { } | |||
class C; | |||
enum E {}; | |||
} | |||
B::V = 1; // 접근자([이름공간명]::[멤버명])를 사용한 내부 멤버 접근 | |||
using B::func; // 내부 멤버를 현재 범위 내에서 접근자를 사용하지 않고 접근 가능 | |||
using namespace B; // 내부 멤버 전체를 현재 범위 내에서 개방 | |||
namespace{ } // 이름이 없는 경우, 전역취급. 접근자는 이름이 없으므로 ::[멤버명] | |||
</source> | |||
< | == 공용체, 열거형 == | ||
공용체의 경우 클래스와 비슷하게 생성자와 소멸자를 사용할 수 있게 되었고, 이름없는 공용체도 만들 수 있다. 다만, C 수준에서 공용체를 다룰 기회가 좀 있었던 것에 비하면, C++의 경우 대부분 low-level 부분에 C 라이브러리를 쓰는 경우가 많아 C++에서 확장된 공용체를 쓸 일은 거의 없다. | |||
int | |||
열거형은 열거 클래스라는 기존 열거형을 확장한 형태를 사용할 수 있게 되었다. | |||
<source lang=cpp> | |||
enum class A : int | |||
{ | { | ||
FIRST = 1, | |||
SECOND = 2 | |||
} | }; | ||
</ | </source> | ||
== 객체 == | |||
C언어의 구조체(struct)를 기억하는가? C언어의 구조체는 다음과 같은 구조이다. | |||
<source lang=cpp> | |||
class [클래스 이름] | |||
{ | |||
struct A | |||
{ | |||
int x; | |||
int y; | |||
}; | |||
</source> | |||
즉 구조체 안에는 변수를 선언할 수 있다. 그런데 C++에서는 struct에 변수 뿐만 아니라 함수의 선언도 허용하고 있다. 즉 다음과 같은 코딩이 가능하다. | |||
<source lang=cpp> | |||
#include<iostream> | |||
struct A | |||
{ | |||
int x; | |||
int y; | |||
void func(){ | |||
std::cout<<x<<y<<std::endl; | |||
} | |||
}; | |||
int main() | |||
{ | |||
A a={1, 2}; | |||
a.func(); | |||
return 0; | |||
} | |||
</source> | |||
C++ | 이 코드를 자세히 보면 struct 내부에 함수의 선언이 가능할 뿐만 아니라, 구조체 자료형의 선언 방법도 C와 다르다. C에서라면 struct A a라고 선언했겠지만, C++에서는 A a라고 선언하는 것을 허용한다. 또한 struct A 내부의 함수인 func는 main 함수에서 마치 구조체 내부의 변수처럼 a 뒤에 점(.)만 찍으면 접근할 수 있다. | ||
이것이 C++에서 struct의 새로운 기능이다. 또한 여기서 a와 같은 것들을 객체(오브젝트)라고 부른다. | |||
한편 c++에서는 '접근지정자'라는 것을 struct 안에 넣을 수 있다. 다음 코드를 보자. | |||
또한 | |||
<source lang=cpp> | |||
#include<iostream> | |||
struct A | |||
std::cout<< | { | ||
private: | |||
int x; | |||
int y; | |||
public: | |||
void func(){ | |||
std::cout<<x<<y<<std::endl; | |||
} | |||
}; | |||
int main() | |||
{ | |||
A a={1, 2}; | |||
/* | |||
a.x=0; | |||
*/ | |||
//↑ 컴파일 에러 발생 | |||
a.func(); | |||
return 0; | |||
} | |||
</source> | |||
이 코드에서는 a.x에 0을 대입하면 컴파일 오류가 뜨는데, private는 아래에 선언된 변수와 함수들이 오직 같은 struct 내부에서만 접근 가능하도록 한다. 따라서 func함수는 x와 y에 접근할 수 있지만 이 struct 밖에 있는 main 함수에서는 x에 접근할 수 없는 것이다. public은 아래에 선언된 변수와 함수들이 struct 밖에서도 접근 가능하도록 한다. 또한 private와 public 외에도 protected라는 것이 있는데, 이것은 상속을 배우고 나서야 이해되는 개념이니 일단은 더이상의 자세한 설명은 생략하겠다. | |||
그리고 c++에서는 struct 대신에 class라고 쓸 수 있다. | |||
즉 위의 코드를 | |||
class A | |||
{ | |||
private: | |||
int x; | |||
int y; | |||
public: | |||
void func(){ | |||
std::cout<<x<<y<<std::endl; | |||
} | |||
}; | |||
int main() | |||
{ | |||
= | A a={1, 2}; | ||
a.func(); | |||
return 0; | |||
} | |||
</source> | |||
이렇게도 쓸 수 있는 것이다. 단지 struct를 class로 교체한 것 뿐이지만, 두 코드는 완벽하게 기능이 똑같다. 물론 struct와 class에도 기능의 차이는 있는데, struct는 기본적인 접근지정자가 public이지만 class는 private이다. 차이는 오직 이것 하나뿐이다. | |||
=== 용어 === | |||
=== 접근지정자 === | === 접근지정자 === | ||
=== const 멤버 === | |||
=== static 멤버 === | |||
=== 생성자 === | |||
==== 일반 생성자 ==== | |||
==== 복사 생성자 ==== | |||
==== 이동 생성자 ==== | |||
=== 소멸자 === | |||
=== 연산자 오버로딩 === | |||
=== 상속 === | |||
==== 명시적 상속 ==== | |||
==== 암시적 상속 ==== | |||
==== 다중상속 ==== | |||
=== 동적 생성 === | |||
== 예외처리 == | == 예외처리 == | ||
=== 원리 === | |||
=== 사용법 === | |||
== 템플릿 == | |||
<s>컴파일 타임을 잡아먹는 주범</s> | |||
일반화 프로그래밍을 다룬다. [[템플릿 메타프로그래밍]]은 해당 항목 참조. | |||
=== 템플릿 함수 === | |||
=== 템플릿 클래스 === | |||
== 람다 == | |||
=== 람다의 전신, 함수자 === | |||
=== 람다와 함수 포인터 === | |||
=== 람다의 사용법 === | |||
== 고급 과정 == | == 고급 과정 == | ||
{{각주}} | {{각주}} | ||
{{쉽게 배우는 프로그래밍 입문}} | |||