[include(틀:관련 문서, top1=C언어/문법)] [include(틀:상위 문서, top1=C++)] [include(틀:문자 입력시 주의, c1=\\0, c2=\\\\0)] [include(틀:프로그래밍 언어 문법)] [목차] == 개요 == [[C++]]의 문법을 간략하게 설명하는 문서이다. [[C++]]은 [[C언어]]의 문법적인 요소에서 약 95% 정도를 계승하고 [[객체 지향 프로그래밍]]을 위해 C++에서만 존재하는 추가적인 문법 요소가 있다. C언어하고도 중첩되는 요소들이 많으므로 이 문서를 쉽게 이해하기 위해서는 [[C언어/문법]] 문서와 비교하여 참조하는 것이 좋다. 하지만 C 관련 문법 + 객체지향 문법만 안다고 해서 C++를 잘 아는건 아니다. 일단 C++11 이후로 추가된 기능이 엄청나게 많기 때문. STL 공부도 많이 해야 한다. 여기에 쓰여있는것들은 어디까지나 기본적인 문법인것을 명심하자. == 편집 지침 == 소스 코드로 예시를 들 때 ||\{{{#!syntax cpp (소스코드) }}}|| 문법을 활용하여 소스코드를 써주시기 바랍니다. ---- '''예시''' {{{#!syntax cpp int main() { std::cout << u8"hello world" << std::endl; return 0; } }}} == 자료형 == C언어의 자료형을 거의 그대로 쓸 수 있다. C++에서는 C언어의 자료형을 관련 함수까지 포함하여 클래스의 형태로 확장시킨 새로운 자료형이 존재한다. 여기서는 C++만의 추가적인 자료형만을 설명하기로 한다. ||<table align=center>'''자료형 이름'''||'''자료형 구조'''||'''그 안에 들어갈 정보'''||'''표준'''|| ||bool||자료형||true 혹은 false||레거시|| ||bitset<N>||정적 컨테이너||비트||레거시|| ||char||배열||u8 리터럴로 선언할 경우 UTF-8 문자열로 인코딩||C++11|| ||char8_t||배열||UTF-8 문자열||C++20|| ||char16_t||배열||UTF-16 문자열||C++11|| ||char32_t||배열||UTF-32 문자열||C++11|| ||string||문자열 컨테이너||문자열||레거시|| ||list<X>||연결 리스트||X에 들어갈 임의의 자료형||레거시|| ||vector<X>||동적 컨테이너||X에 들어갈 임의의 자료형||레거시|| ||auto||추론||초기화된 변수||C++11|| ||decltype(X)||추론||X에 들어갈 임의의 자료형||C++11|| == 클래스 == {{{#!syntax cpp class MyParent { protected: string text = u8"Hello World"; public: void print() { cout << text << endl; } } }}} 클래스는 예시와 같이 작성한다. 완성된 소스 코드는 아니고 클래스 부분에 대해서만 예시로 작성하였다. '''{{{class [클래스 이름]}}}'''으로 클래스를 정의할 수 있다. {{{private}}}, {{{protected}}}, {{{public}}}은 클래스 멤버 (클래스 내부의 자료 및 함수)에 대한 접근 권한을 지정하는 지시자이다. {{{private}}}는 해당 클래스의 멤버 함수만 접근할 수 있음을 뜻하고, {{{protected}}}는 자신의 멤버 함수 및 해당 클래스를 상속받는 자식 클래스의 멤버 함수만 접근할 수 있음을 뜻한다. {{{public}}}은 해당 클래스의 외부에서 접근할 수 있다는 것을 뜻한다. === {{{this}}} 포인터 === 객체 지향 언어에서는 멤버 함수를 구현할 때에 매개변수의 자료형과 이름을 멤버 함수의 자료형과 이름과 동일하게 사용해도 혼선이 생기지 않는다. 다만, 멤버 변수와 멤버 함수 내의 지역 변수를 구분하기 위해서 C++에서는 {{{this}}} 포인터를 활용할 수 있다. this 포인터는 해당 클래스로 생성된 인스턴스 자기 자신의 주소를 리턴한다. === 생성자 === {{{#!syntax cpp class MyClass { public: MyClass(...) { [생성자 내용] } } }}} 생성자는 클래스의 생성 시점에서 객체의 초기화를 위하여 실행되는 함수이다. 위의 예제처럼 함수 이름이 클래스 이름하고 똑같고 리턴값의 형태를 명시하지 않는다. 즉, 함수의 원형을 '''클래스 이름(매개 변수)'''라고 정의하면 된다. ==== 이니셜라이저 ==== {{{#!syntax cpp class MyClass { private: int a, b, c; public: void func(int l, int m, int n) : a(l), b(m), c(n) { ... } } }}} 이 예제에서 함수의 원형 뒤에 오는 '''{{{: a(l), b(m), c(n)}}}'''이 '''이니셜라이저'''이다. 멤버 함수의 실행 시점에서 매개 변수에 맞춰서 멤버 변수를 초기화하는 기능을 수행한다. 이 예제에서는 이니셜라이저를 통해 {{{a=l}}}, {{{b=m}}}, {{{c=n}}}으로 대입 연산이 실행되었다. 한편, 이니셜라이저는 멤버 변수로써 클래스 인스턴스의 초기화도 가능하다. {{{#!syntax cpp class MyClass { private: MyObj obj; public: void func(int n) : obj(n) { ... } } }}} 이 예제에서는 '''{{{: obj(n)}}}'''이 이니셜라이저이다. {{{obj}}}라는 {{{MyObj}}} 클래스의 인스턴스가 이니셜라이저를 통해 {{{obj(int x)}}}라는 생성자를 자동적으로 호출하여 초기화된다. 정리하자면 이니셜라이저는 함수의 원형 뒤에 바로 정의하며 ''': [초기화 할 멤버 변수 1](매개 변수), [초기화 할 멤버 변수 2](매개 변수), ...'''라고 적으면 된다. 이니셜라이저를 잘 활용하면 생성자를 사용하는 번거로움을 줄일 수 있다. ==== 복사 생성자 ==== ===== 얕은 복사 ===== ===== 깊은 복사 ===== ==== 이동 생성자 ==== === 소멸자 === 소멸자는 클래스의 소멸 시점에서 {{{malloc}}} 함수 및 {{{new}}} 지시자를 통해 동적할당된 멤버 변수에 대한 메모리 반환을 위하여 실행되는 함수이다. 함수의 원형을 '''~클래스 이름(매개 변수)'''라고 정의하면 된다. 소멸자의 목적이 동적할당에 대한 메모리 자원의 반환인 만큼 소멸자에서는 거의 매개 변수를 쓸 일이 없다. {{{#!syntax cpp class MyClass { private: char* str; public: MyClass() { str = new char[100]; } ~MyClass(){ delete[] str; } } }}} 위 예제의 경우 '''{{{~MyClass()}}}'''가 소멸자이다. 생성자 '''{{{MyClass()}}}'''에서 동적할당된 {{{char}}} 자료형인 {{{str}}}에 대해서 소멸자 {{{~MyClass()}}}에서 {{{delete[]}}}를 통해 메모리를 수동적으로 반환한다. == [[네임스페이스]] == '''네임스페이스''' (namespace, 직역한 표현으로는 '''이름 공간''')는 (동일한)이름 충돌을 막기 위한 장치이다. [[자바]]에서는 패키지가 해당 역할을 담당한다. 모든 [[객체 지향 프로그래밍|객체 지향 언어]]가 그렇듯 C++에서도 이름이 같은 두 클래스가 네임스페이스가 서로 다르면 공존할 수 있다. 중규모 이상의 프로그램을 코딩할 때에는 네임스페이스를 쓸 때가 많으며 각 객체에 대한 네이밍 문제를 상당 부분 해결해준다. 예시이다. {{{#!syntax cpp #include <iostream> #include <string> #include <cuchar> using namespace std; namespace namu { class MyClass { private: string text = u8"나무위키"; public: void print() { cout << text << endl; } }; } namespace wiki { class MyClass { private: string text = u8"Namu Wiki"; public: void print() { cout << text << endl; } }; } int main() { namu::MyClass class1; wiki::MyClass class2; class1.print(); class2.print(); return 0; } }}} 이 소스 코드의 실행 결과는 다음과 같다. ||<table align=center> '''나무위키''' [br] '''Namu Wiki''' || 첫번째 줄에는 '나무위키'가 출력되고 두번째 줄에는 'Namu Wiki'가 출력된다. === 전역 네임스페이스 === [[C++/문법#s-5|항목 5]]의 예시와 동일한 결과가 나오지만 전역 네임스페이스만 사용하도록 소스 코드를 변경하였다. {{{#!syntax cpp #include <iostream> #include <string> #include <cuchar> using namespace std; class namu { private: string text = u8"나무위키"; public: void print() { cout << text << endl; } }; class wiki { private: string text = u8"Namu Wiki"; public: void print() { cout << text << endl; } }; int main(void) { namu class1; wiki class2; class1.print(); class2.print(); return 0; } }}} 이렇게 명시적으로 네임스페이스 선언이 되어있지 않은 상태에서는 전역 네임스페이스를 사용하게 된다. === 지시자 {{{using}}} === 지시자 {{{using}}}을 통해서 클래스를 선언할 때에 축약된 표현을 사용할 수 있다. {{{include}}} 선언부 다음에 {{{using [네임스페이스 이름]::[네임스페이스 멤버 이름];}}}[* 네임스페이스 멤버로는 클래스, 클래스 멤버 함수가 있다.]이라고 적은 경우를 생각해보자. {{{using [네임스페이스 x]::[클래스 y];}}}가 선언된 경우에는 클래스 y에 대한 인스턴스를 선언할 때에 {{{x::y [인스턴스 이름];}}}이라고 적지 않고 {{{y [인스턴스 이름]}}}이라고 적을 수 있다. {{{using [네임스페이스 x]::[멤버 함수 a];}}}가 선언된 경우에는 해당 함수를 호출할 때에 {{{x::a(...);}}}이 아니라 {{{a(...);}}}으로 호출할 수 있다. {{{include}}} 선언부 다음에 {{{using namespace [네임스페이스 이름]}}}을 적을 경우에는 클래스에 대한 인스턴스를 선언할 때에 {{{[네임스페이스 이름]::[클래스 이름] [인스턴스 이름];}}}이라고 적지 않고 {{{[클래스 이름] [인스턴스 이름];}}}이라고 적어도 된다. 이 경우에는 네임스페이스를 명시하지 않고도 해당 네임스페이스 안에 있는 모든 멤버를 호출할 수 있다. {{{using namespace [네임스페이스 x]}}}가 선언된 경우에는 이미 {{{using [네임스페이스 x]::[클래스 y];}}}나 {{{using [네임스페이스 x]::[멤버 함수 a];}}}를 선언한 것과 같아서 {{{x::y [인스턴스 이름];}}} 대신에 바로 {{{y [인스턴스 이름]}}}이라고 인스턴스를 생성할 수 있고, {{{x::a(...);}}}이 아니라 {{{a(...);}}}로 클래스 멤버 함수를 바로 호출할 수 있다. C++에서 가장 많이 선언되는 {{{using namespace std;}}}의 경우에는 C++의 표준 라이브러리에 대한 네임스페이스인 {{{std}}}를 명시하지 않고 생략해서 해당 네임스페이스에 있는 클래스 멤버를 사용하겠다는 뜻이다. 예를 들어, 이 구문이 선언되어있는 경우에는 {{{std::cout}}} 대신에 {{{cout}}}라고 축약해서 명시할 수 있다. [[C++/문법#s-5|항목 5]]의 예제에서 {{{include}}} 선언부 다음에 '''{{{using namespace}}}''' {{{namu;}}}를 적을 경우에는 {{{main}}} 함수에서 {{{namu}}} 네임스페이스에 있는 {{{MyClass}}} 클래스에 대한 {{{class1}}} 인스턴스를 선언할 때에 {{{namu::MyClass class1;}}}라고 적지 않고 {{{MyClass class1;}}}라고 축약해서 적을 수 있다. == 소스 코드 작성 == [[C++/문법#s-5|항목 5]]의 소스 코드처럼 작성한다. {{{[include 부분]}}} {{{[클래스 정의]}}} {{{[main 함수 정의]}}} 단일 소스 코드로 작성할 때에는 위와 같은 순서대로 작성한다. == 소스 코드 분할 == 네임스페이스를 사용할 때에는 다음과 같이 {{{main}}} 함수 소스, 클래스 선언 소스, 클래스 구현 소스로 분할한다. [[C++/문법#s-5|항목 5]]의 예제를 토대로 설명한다. === 클래스 선언 소스 === {{{#!syntax cpp #ifndef MYCLASS_H #define MYCLASS_H #include <string> #include <cuchar> namespace namu { class MyClass { private: std::string text=u8"나무위키"; // C++11 이상부터 기본 멤버 초기화자 사용 가능 public: void print(); }; } namespace wiki { class MyClass { private: std::string text=u8"Namu Wiki"; public: void print(); }; } #endif }}} 여기서는 클래스의 멤버함수들 및 멤버들을 선언한다. 필요한 헤더를 {{{include}}}시키고 {{{private}}}, {{{public}}}, {{{protected}}} 접근 권한에 따라서 클래스 내부에서 사용할 변수와 함수의 원형을 선언하면 된다. 확장자는 {{{*.h}}} 혹은 {{{*.hpp}}}가 일반적이며, C++ 헤더로 작성한다. 이 헤더 파일에서 {{{using namespace std;}}}를 사용했다가는, 헤더 파일을 {{{include}}}하는 다른 모든 파일에도 {{{std}}} 네임스페이스가 적용되어 코드에 오류가 발생할 수 있으므로 주의해야 한다. 헤더 파일에서는 네임스페이스명을 생략하지 말고, {{{std::}}}를 입력해주어야 한다. === 클래스 구현 소스 === {{{#!syntax cpp #include <iostream> #include "[클래스 선언 소스.h]" using namespace std; void namu::MyClass::print() { cout << text << endl; } void wiki::MyClass::print() { cout << text << endl; } }}} 여기서는 클래스 선언 소스에서 선언한 함수의 원형을 구현한다. 필요한 헤더와 클래스 선언 소스를 {{{include}}}한 다음에 해당 클래스에 있는 모든 멤버 함수 및 {{{static}}} 멤버(위 예시에서는 없음)를 실제로 구현한다. 확장자는 일반적으로 {{{*.cpp}}}로 작성한다.[* [[비주얼 스튜디오]]에서는 C언어 및 C++ 소스코드에 {{{*.cpp}}} 확장자를 기본으로, G++에서는 {{{*.cc}}} 확장자를 기본으로 사용한다. 애초에 확장자는 프로그래머가 구별하기 쉽게 하기 위함이지 땩히 표준은 없다.] === main 함수 소스 === {{{#!syntax cpp #include "[클래스 선언 소스.h]" int main(void) { namu::MyClass class1; wiki::MyClass class2; class1.print(); class2.print(); return 0; } }}} 필요한 소스 코드나 헤더를 {{{include}}}를 한 다음에 {{{main}}} 함수를 작성한다. {{{main}}} 함수 소스에 클래스를 연동시키려면 클래스 선언 헤더를 {{{include}}}해야 정상적으로 소스 코드가 컴파일된다. 구현 파일 또한 컴파일러에게 잘 전달해 주어야 링크 에러가 나지 않는다. 확장자는 일반적으로 {{{*.cc}}} 또는 {{{*.cpp}}}이다. == [[상속(프로그래밍)|상속]] == {{{#!syntax cpp class MyParent { protected: string text=u8"Hello World"; public: void print(){ cout << text << endl; } } class MyChild : public MyParent { private: int a=128; public: void printC(){ cout << text << u8" " << a << endl; } } }}} 이때 자식 클래스에서는 {{{string}}} 자료형인 변수 {{{text}}}를 별도로 선언하지 않았으나 부모 클래스인 {{{MyParent}}}로부터 해당 변수를 물려받는다. 이것을 '''상속'''이라고 한다.[* 부모 클래스인 {{{MyParent}}} 클래스에서 멤버 변수 {{{text}}}를 {{{protected}}} 권한으로 선언하고, {{{public}}} 상속을 했기 때문에 접근이 가능하다. 자세한 내용은 상속 방식에서 설명한다.] 부모 클래스의 멤버 함수인 {{{print()}}}도 상속받았다. 추가적으로 자식 클래스만이 갖는 멤버인 변수 {{{a}}}와 함수 {{{printC()}}}가 존재한다. 자식 클래스의 원형은 '''{{{class [자식 클래스 이름] : [상속 방식] [부모 클래스 이름]}}}'''로 정의하면 된다. 상위 클래스의 멤버 함수를 호출하고 싶다면 그냥 호출하면 된다. 다만, 이름이 겹치는 메서드의 경우 '''{{{부모 클래스 이름::멤버 함수(매개 변수)}}}'''로 호출해야 한다. === 상속 방식 === [[C++/문법#s-4.1|항목 4.1]]의 예제에서 {{{MyChild}}} 클래스는 부모 클래스인 {{{MyParent}}} 클래스로부터 {{{public}}} 방식으로 상속받았다. 자식 클래스의 상속 방식에서 {{{private}}}, {{{protected}}}, {{{public}}}이 있으며 클래스 멤버에 대한 접근 권한과 혼동하면 안된다. 상속 방식에 따라서 부모 클래스의 멤버를 자식 클래스가 물려받을 때에 자식 클래스에서 접근하는 부모 클래스의 멤버에 대한 접근 권한이 달라진다. ||<table align=center> 권한 별 클래스 멤버 || '''private 상속''' || '''protected 상속''' || '''public 상속''' || || private 멤버 || 접근 불가 || 접근 불가 || 접근 불가 || || protected 멤버 || private 권한 || protected 권한 || protected 권한 || || public 멤버 || private 권한 || protected 권한 || public 권한 || === 다중 상속 === [[C++]]은 [[자바]]와는 달리 '''다중 상속'''이 가능하다. 하나의 자식 클래스가 동시에 여러 개의 부모 클래스를 상속받을 수 있다. 상속받을 부모 클래스의 개수에 제한이 없다. {{{#!syntax cpp class MyChild : public MyMother, public MyFather (){...} }}} 이 예제에서는 클래스의 원형만 간략하게 적었다. {{{MyChild}}}라는 자식 클래스는 {{{MyMother}}}라는 클래스로부터 {{{public}}} 상속을 받고, {{{MyFather}}}라는 클래스로부터 {{{public}}} 상속을 받는다. 다만, 이렇게 다중 상속을 사용할 경우에는 자식 클래스를 구현할 때에 자식 클래스 자기 자신의 멤버인지 아니면 어떤 부모 클래스의 멤버를 사용할지를 정확하게 표현해야할 필요가 있다. [[자바]]와는 달리 C++의 경우에는 다중상속이 가능하다는 특성으로 인해 한단계 상위 클래스를 호출하는 참조자가 존재할 수가 없다. 자바는 다중상속이 불가능하므로 명시적으로 부모 클래스를 밝히지 않고도 부모 클래스의 멤버를 호출할 수 있다. 이와 같은 역할을 하는 것이 '''{{{super}}}'''라는 키워드이다.[* 다만, '''{{{super.super}}}'''처럼 {{{super}}}를 두번 이상 사용하는 것은 [[자바]]에서 문법적으로 허용되지 않는다. 자바에서 두단계 이상 상위 클래스를 호출하고 싶다면 C++처럼 클래스 이름을 직접적으로 명시하여 호출해야 한다. '''{{{상위 클래스 이름.멤버 메소드 이름(매개 변수)}}}''' 이런 식으로 호출해야 한다.] === 가상 함수 === {{{#!syntax cpp class MyParent { protected: string text=u8"Hello World"; public: virtual void print(){ cout << text << endl; } virtual ~MyParent() = default; } class MyChild : public MyParent { private: string str=u8"안녕하세요."; public: void print() override{ cout << str << endl; } } }}} 부모 클래스인 {{{MyParent}}}의 멤버 함수인 {{{print()}}} 함수에서는 멤버 변수인 {{{text}}}의 내용인 '''{{{Hello World}}}'''를 출력한다. 그러나 자식 클래스인 {{{My Child}}}에서는 멤버 변수인 {{{str}}}의 내용인 '''안녕하세요.'''를 출력하도록 {{{print()}}} 함수를 재정의하였다. 자식 클래스에서 부모 클래스의 멤버 함수를 재정의하려면 부모 클래스에는 '''{{{virtual [리턴 형태] [함수명] (매개 변수)}}}'''라고 자식에게 상속할 멤버 함수의 원형을 정의한다. 자식 클래스에서는 '''{{{[리턴 형태] [부모 클래스의 멤버 함수명] ([해당 함수의 멤버 변수]) override}}}'''라고 원형을 정의한다. 이때 부모 클래스의 함수를 '''가상 함수'''라고 한다. C++에서는 가상 함수의 경우에만 동적 바인딩을 지원하며 상속이 가능하다. == 메모리 할당 == === 정적 할당 === === 자동 할당 === 함수/메소드 안에서 값/객체를 생성하면 일반적으로 자동 메모리 할당이다. 예를 들면 {{{#!syntax cpp #include <iostream> using namespace std; int main() { int x = 0; cout << x << endl; //0 return 0; } }}} 에서 "{{{int x = 0}}}"가 컴퓨터 메모리를 정수에 할당하는것이다. 자동 할당은 컴퓨터의 메모리중 스택이라는 곳에 저장된다. 이렇게 만들어진 자료는 함수의 범위가 끝나면 할당된 메모리가 회수된다. 따라서 다음같은 코드는 C++에선 먹히지 않는다. {{{#!syntax cpp int* zero_ptr(){ int x = 0; int *ptr = &x; // Dangling pointer return ptr; //에러 } }}} 이 코드의 의도는 '0'을 가리키는 포인터를 반환시키는 함수를 만드는것이다. 일단 자동 할당으로 {{{x}}}를 생성하고, 정수 포인터인 {{{ptr}}}에게 {{{x}}}의 메모리 주소를 가리키라고 명령하고 {{{ptr}}}를 반환하는것. 하지만 위에서 서술했듯이, {{{x}}}는 자동 할당으로 만들어진 자료기 때문에 {}로 정의된 함수의 범위 (scope)를 벋어나면 {{{x}}}에게 주어진 메모리는 컴퓨터가 회수한다. 요약하자면 실제로 이 함수를 사용하면 나오는건 0을 가리키는 포인터가 아니라 아무 말도 안 되는 걸 가리키는, 사용할 수 없는 포인터다. 그렇다면 이걸 어떻게 고쳐야 할까? 답은 동적 메모리 할당 (dynamic memory allocation)이다. === 동적 할당 === 동적 할당은 스택 (stack) 대신 힙(heap)이란 메모리를 사용한다. 힙 영역에 메모리를 할당하려면 "{{{new}}}" 표현식을 사용하면 된다. 동적 메모리 할당을 하려면 "{{{T *ptr = new T}}}"같은 식으로 쓰자. 예제로, 위에서 의도한 함수를 만들려면 다음과 같이 코드를 짜면 된다. {{{#!syntax cpp int* zero_ptr(){ int *ptr = new int; //동적 할당 *ptr = 0; return ptr; } }}} 단 여기서 주의해야 할것이 있는데, 힙의 메모리를 자료에 할당해주면 이 자료는 써먹은 다음에 반드시 풀어주어야 한다. 그렇지 않으면 메모리 누수가 일어 나게 된다. "{{{new}}}"로 동적 할당한 메모리는 "{{{delete}}}" 지시자로 풀어주자. {{{#!syntax cpp #include <iostream> using namespace std; //.. zero_ptr() 함수를 전같이 정의.. int main(){ int *ptr = zero_ptr(); //동적 할당 cout << *ptr << endl; //0 delete ptr; //ptr가 가리키는 메모리를 풀어줌. 이제 ptr은 재정의 되기 전까진 사용할 수 없다. return 0; } }}} 동적 메모리 할당은 자동/정적 할당보다 주의가 요구 된다. 정수 몇개 처럼 적은 메모리 용량을 차지하는 자료라면 별 상관 없겠지만, 좀 더 방대한 자료를 다루는 프로그램이라면 필요없는 자료를 풀어주지 않았다가 맛이 가버리는 수가 있다. 그래서 현대(C++11 이상)에서는 소유권을 갖는 raw 포인터의 사용을 금지하도록 권고된다. 소유권 전달 시 반드시 스마트 포인터를 사용하자. 또한 서로 다른 C++ 런타임을 사용하는 코드가 있는 경우 해제({{{delete}}})는 할당({{{new}}})한 프로그램이 사용하는 런타임 영역 내에서 이루어 져야 한다. GCC가 사용하는 libstdc++와 llvm이 사용하는 libc++, 그리고 MSVC가 사용하는 msvcp는 C 런타임과 다르게 서로 다른 ABI를 가지고 있으며 서로 할당 방법이 다르다. 예를 들어 libstdc++에서 할당한 포인터를 msvcp가 해제하는 경우 힙 커럽션등의 문제가 생길 수 있다. 마찬가지로 얼로케이터를 사용한 할당이나 아래의 libc 스타일의 malloc으로 할당한 포인터 또한 C++의 {{{delete}}}로 해제해서는 안 된다. 그 반대도 마찬가지. {{{placement new}}} 를 이용하여 이미 할당 된 메모리(스택도 포함)에 메모리를 할당 할 수도 있다 해당 방법으로 스택 메모리에다가 할당한 경우, 따로 해제를 해 주지 않아도 된다(다만, 클래스라면 소멸자는 호출 해 주어야 한다). ==== C언어 방식 ==== [include(틀:상세 내용, 문서명=C언어/문법, 앵커=동적 할당)] [[C언어/문법#동적 할당]] 문서의 '''동적 할당''' 문단을 참조하면 된다. == 호출 방식 == 후술할 예시의 {{{increment}}} 함수 선언에 따라 값에 의한 호출, 주소에 의한 호출, 참조에 의한 호출이 있다. === 값에 의한 호출 === Pass(Call) by value 함수의 패러미터로 사용하기 위해 자료를 값에 의해 호출하는것. 여기서 중요한것은, 값에 의해 호출하면 해당 자료는 함수의 범위내에서 직접 사용되지 않고, 복사되어서 사용된다. 예를 들자면 {{{#!syntax cpp #include <iostream> void increment1(int x){ //값에 의한 호출. ++x; //무의미 } int main(){ int a = 0; increment1(a); //아무것도 안 함. std::cout << a; //0 return 0; } }}} {{{increment1}}} 함수는 {{{a}}}를 {{{x}}}에 복사해서, {{{x}}}를 증가시키기 때문에 오리지널 {{{a}}}에는 아무런 영향을 주지 않는다. === 주소에 의한 호출 === Pass(Call) by address(pointer) 자료의 값이 아닌 주소 (포인터)[* 밑의 예시에서는 a의 메모리 주소를 가르키는 x 포인터를 사용했다]를 호출. 실제 {{{void increment}}} 함수를 만들 수 있는 방법 중 하나다. {{{#!syntax cpp #include <iostream> void increment (int *x){ //주소에 의한 호출 ++(*x); } int main(){ int a = 0; increment(&a); std::cout << a; //1 return 0; } }}} 값에 의한 호출과는 다르게 주소에 의한 호출은 포인터를 사용함으로써 오리지날 자료를 복사하지 않는다. 큰 자료를 함수의 패러미터로 사용할 생각이라면 복사하느라 메모리 낭비를 안 해도 되니 괜찮은 수법이다. === [[참조에 의한 호출]] === [include(틀:상세 내용, 문서명=참조에 의한 호출)] Pass(Call) by reference. {{{void increment}}} 같은 함수를 만들 수 있는 또 하나의 방법이다. {{{#!syntax cpp #include <iostream> void increment (int &x){ //참조에 의한 호출. ++x; //여기서 x는 복제체가 아님. } int main(){ int a = 0; increment(a); std::cout << a; //1 return 0; } }}} 참조에 의해 호출을 하면 패러미터가 함수 범위내에서 복제되지 않고 원본이 그대로 사용된다. [[분류:프로그래밍 언어 문법]]