C++/문법
최근 수정 시각: (5년 전)
1. 개요 [편집]
C++의 문법을 간략하게 설명하는 문서이다.
C++은 C언어의 문법적인 요소에서 약 95% 정도를 계승하고 객체 지향 프로그래밍을 위해 C++에서만 존재하는 추가적인 문법 요소가 있다.
C언어하고도 중첩되는 요소들이 많으므로 이 문서를 쉽게 이해하기 위해서는 C언어/문법 문서와 비교하여 참조하는 것이 좋다. 하지만 C 관련 문법 + 객체지향 문법만 안다고 해서 C++를 잘 아는건 아니다. 일단 C++11 이후로 추가된 기능이 엄청나게 많기 때문. STL 공부도 많이 해야 한다. 여기에 쓰여있는것들은 어디까지나 기본적인 문법인것을 명심하자.
C++은 C언어의 문법적인 요소에서 약 95% 정도를 계승하고 객체 지향 프로그래밍을 위해 C++에서만 존재하는 추가적인 문법 요소가 있다.
C언어하고도 중첩되는 요소들이 많으므로 이 문서를 쉽게 이해하기 위해서는 C언어/문법 문서와 비교하여 참조하는 것이 좋다. 하지만 C 관련 문법 + 객체지향 문법만 안다고 해서 C++를 잘 아는건 아니다. 일단 C++11 이후로 추가된 기능이 엄청나게 많기 때문. STL 공부도 많이 해야 한다. 여기에 쓰여있는것들은 어디까지나 기본적인 문법인것을 명심하자.
2. 편집 지침 [편집]
소스 코드로 예시를 들 때
{{{#!syntax cpp (소스코드) }}} |
문법을 활용하여 소스코드를 써주시기 바랍니다.
예시
int main() {
std::cout << u8"hello world" << std::endl;
return 0;
}3. 자료형 [편집]
C언어의 자료형을 거의 그대로 쓸 수 있다. C++에서는 C언어의 자료형을 관련 함수까지 포함하여 클래스의 형태로 확장시킨 새로운 자료형이 존재한다. 여기서는 C++만의 추가적인 자료형만을 설명하기로 한다.
자료형 이름 | 자료형 구조 | 그 안에 들어갈 정보 | 표준 |
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 |
4. 클래스 [편집]
class MyParent {
protected:
string text = u8"Hello World";
public:
void print() {
cout << text << endl;
}
}클래스는 예시와 같이 작성한다. 완성된 소스 코드는 아니고 클래스 부분에 대해서만 예시로 작성하였다.
class [클래스 이름]으로 클래스를 정의할 수 있다. private, protected, public은 클래스 멤버 (클래스 내부의 자료 및 함수)에 대한 접근 권한을 지정하는 지시자이다. private는 해당 클래스의 멤버 함수만 접근할 수 있음을 뜻하고, protected는 자신의 멤버 함수 및 해당 클래스를 상속받는 자식 클래스의 멤버 함수만 접근할 수 있음을 뜻한다. public은 해당 클래스의 외부에서 접근할 수 있다는 것을 뜻한다.4.1. this 포인터
[편집]
객체 지향 언어에서는 멤버 함수를 구현할 때에 매개변수의 자료형과 이름을 멤버 함수의 자료형과 이름과 동일하게 사용해도 혼선이 생기지 않는다. 다만, 멤버 변수와 멤버 함수 내의 지역 변수를 구분하기 위해서 C++에서는
this 포인터는 해당 클래스로 생성된 인스턴스 자기 자신의 주소를 리턴한다.
this 포인터를 활용할 수 있다.this 포인터는 해당 클래스로 생성된 인스턴스 자기 자신의 주소를 리턴한다.
4.2. 생성자 [편집]
class MyClass {
public:
MyClass(...) {
[생성자 내용]
}
}생성자는 클래스의 생성 시점에서 객체의 초기화를 위하여 실행되는 함수이다. 위의 예제처럼 함수 이름이 클래스 이름하고 똑같고 리턴값의 형태를 명시하지 않는다. 즉, 함수의 원형을 클래스 이름(매개 변수)라고 정의하면 된다.
4.2.1. 이니셜라이저 [편집]
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으로 대입 연산이 실행되었다.한편, 이니셜라이저는 멤버 변수로써 클래스 인스턴스의 초기화도 가능하다.
class MyClass {
private:
MyObj obj;
public:
void func(int n) : obj(n) { ... }
}이 예제에서는
: obj(n)이 이니셜라이저이다. obj라는 MyObj 클래스의 인스턴스가 이니셜라이저를 통해 obj(int x)라는 생성자를 자동적으로 호출하여 초기화된다.정리하자면 이니셜라이저는 함수의 원형 뒤에 바로 정의하며 : [초기화 할 멤버 변수 1](매개 변수), [초기화 할 멤버 변수 2](매개 변수), ...라고 적으면 된다.
이니셜라이저를 잘 활용하면 생성자를 사용하는 번거로움을 줄일 수 있다.
4.2.2. 복사 생성자 [편집]
4.2.2.1. 얕은 복사 [편집]
4.2.2.2. 깊은 복사 [편집]
4.2.3. 이동 생성자 [편집]
4.3. 소멸자 [편집]
소멸자는 클래스의 소멸 시점에서
위 예제의 경우
malloc 함수 및 new 지시자를 통해 동적할당된 멤버 변수에 대한 메모리 반환을 위하여 실행되는 함수이다. 함수의 원형을 ~클래스 이름(매개 변수)라고 정의하면 된다. 소멸자의 목적이 동적할당에 대한 메모리 자원의 반환인 만큼 소멸자에서는 거의 매개 변수를 쓸 일이 없다.class MyClass {
private:
char* str;
public:
MyClass() {
str = new char[100];
}
~MyClass(){
delete[] str;
}
}위 예제의 경우
~MyClass()가 소멸자이다. 생성자 MyClass()에서 동적할당된 char 자료형인 str에 대해서 소멸자 ~MyClass()에서 delete[]를 통해 메모리를 수동적으로 반환한다.5. 네임스페이스 [편집]
네임스페이스 (namespace, 직역한 표현으로는 이름 공간)는 (동일한)이름 충돌을 막기 위한 장치이다. 자바에서는 패키지가 해당 역할을 담당한다.
모든 객체 지향 언어가 그렇듯 C++에서도 이름이 같은 두 클래스가 네임스페이스가 서로 다르면 공존할 수 있다.
중규모 이상의 프로그램을 코딩할 때에는 네임스페이스를 쓸 때가 많으며 각 객체에 대한 네이밍 문제를 상당 부분 해결해준다.
예시이다.
이 소스 코드의 실행 결과는 다음과 같다.
모든 객체 지향 언어가 그렇듯 C++에서도 이름이 같은 두 클래스가 네임스페이스가 서로 다르면 공존할 수 있다.
중규모 이상의 프로그램을 코딩할 때에는 네임스페이스를 쓸 때가 많으며 각 객체에 대한 네이밍 문제를 상당 부분 해결해준다.
예시이다.
#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;
}이 소스 코드의 실행 결과는 다음과 같다.
나무위키 Namu Wiki |
첫번째 줄에는 '나무위키'가 출력되고 두번째 줄에는 'Namu Wiki'가 출력된다.
5.1. 전역 네임스페이스 [편집]
항목 5의 예시와 동일한 결과가 나오지만 전역 네임스페이스만 사용하도록 소스 코드를 변경하였다.
이렇게 명시적으로 네임스페이스 선언이 되어있지 않은 상태에서는 전역 네임스페이스를 사용하게 된다.
#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;
}이렇게 명시적으로 네임스페이스 선언이 되어있지 않은 상태에서는 전역 네임스페이스를 사용하게 된다.
5.2. 지시자 using
[편집]
지시자
C++에서 가장 많이 선언되는
항목 5의 예제에서
using을 통해서 클래스를 선언할 때에 축약된 표현을 사용할 수 있다.include 선언부 다음에 using [네임스페이스 이름]::[네임스페이스 멤버 이름];[1]이라고 적은 경우를 생각해보자. 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라고 축약해서 명시할 수 있다.항목 5의 예제에서
include 선언부 다음에 using namespace namu;를 적을 경우에는 main 함수에서 namu 네임스페이스에 있는 MyClass 클래스에 대한 class1 인스턴스를 선언할 때에 namu::MyClass class1;라고 적지 않고 MyClass class1;라고 축약해서 적을 수 있다.6. 소스 코드 작성 [편집]
7. 소스 코드 분할 [편집]
네임스페이스를 사용할 때에는 다음과 같이
main 함수 소스, 클래스 선언 소스, 클래스 구현 소스로 분할한다. 항목 5의 예제를 토대로 설명한다.7.1. 클래스 선언 소스 [편집]
#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::를 입력해주어야 한다.7.2. 클래스 구현 소스 [편집]
#include <iostream>
#include "[클래스 선언 소스.h]"
using namespace std;
void namu::MyClass::print()
{
cout << text << endl;
}
void wiki::MyClass::print()
{
cout << text << endl;
}여기서는 클래스 선언 소스에서 선언한 함수의 원형을 구현한다.
필요한 헤더와 클래스 선언 소스를
include한 다음에 해당 클래스에 있는 모든 멤버 함수 및 static 멤버(위 예시에서는 없음)를 실제로 구현한다.확장자는 일반적으로
*.cpp로 작성한다.[2]7.3. main 함수 소스 [편집]
#include "[클래스 선언 소스.h]"
int main(void)
{
namu::MyClass class1;
wiki::MyClass class2;
class1.print();
class2.print();
return 0;
}필요한 소스 코드나 헤더를
include를 한 다음에 main 함수를 작성한다. main 함수 소스에 클래스를 연동시키려면 클래스 선언 헤더를 include해야 정상적으로 소스 코드가 컴파일된다. 구현 파일 또한 컴파일러에게 잘 전달해 주어야 링크 에러가 나지 않는다.확장자는 일반적으로
*.cc 또는 *.cpp이다.8. 상속 [편집]
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로부터 해당 변수를 물려받는다. 이것을 상속이라고 한다.[3] 부모 클래스의 멤버 함수인 print()도 상속받았다.추가적으로 자식 클래스만이 갖는 멤버인 변수
a와 함수 printC()가 존재한다.자식 클래스의 원형은
class [자식 클래스 이름] : [상속 방식] [부모 클래스 이름]로 정의하면 된다.상위 클래스의 멤버 함수를 호출하고 싶다면 그냥 호출하면 된다.
다만, 이름이 겹치는 메서드의 경우
부모 클래스 이름::멤버 함수(매개 변수)로 호출해야 한다.8.1. 상속 방식 [편집]
항목 4.1의 예제에서
자식 클래스의 상속 방식에서
MyChild 클래스는 부모 클래스인 MyParent 클래스로부터 public 방식으로 상속받았다.자식 클래스의 상속 방식에서
private, protected, public이 있으며 클래스 멤버에 대한 접근 권한과 혼동하면 안된다. 상속 방식에 따라서 부모 클래스의 멤버를 자식 클래스가 물려받을 때에 자식 클래스에서 접근하는 부모 클래스의 멤버에 대한 접근 권한이 달라진다.권한 별 클래스 멤버 | private 상속 | protected 상속 | public 상속 |
private 멤버 | 접근 불가 | 접근 불가 | 접근 불가 |
protected 멤버 | private 권한 | protected 권한 | protected 권한 |
public 멤버 | private 권한 | protected 권한 | public 권한 |
8.2. 다중 상속 [편집]
C++은 자바와는 달리 다중 상속이 가능하다. 하나의 자식 클래스가 동시에 여러 개의 부모 클래스를 상속받을 수 있다. 상속받을 부모 클래스의 개수에 제한이 없다.
이 예제에서는 클래스의 원형만 간략하게 적었다.
다만, 이렇게 다중 상속을 사용할 경우에는 자식 클래스를 구현할 때에 자식 클래스 자기 자신의 멤버인지 아니면 어떤 부모 클래스의 멤버를 사용할지를 정확하게 표현해야할 필요가 있다.
자바와는 달리 C++의 경우에는 다중상속이 가능하다는 특성으로 인해 한단계 상위 클래스를 호출하는 참조자가 존재할 수가 없다.
자바는 다중상속이 불가능하므로 명시적으로 부모 클래스를 밝히지 않고도 부모 클래스의 멤버를 호출할 수 있다. 이와 같은 역할을 하는 것이
class MyChild : public MyMother, public MyFather (){...}이 예제에서는 클래스의 원형만 간략하게 적었다.
MyChild라는 자식 클래스는 MyMother라는 클래스로부터 public 상속을 받고, MyFather라는 클래스로부터 public 상속을 받는다.다만, 이렇게 다중 상속을 사용할 경우에는 자식 클래스를 구현할 때에 자식 클래스 자기 자신의 멤버인지 아니면 어떤 부모 클래스의 멤버를 사용할지를 정확하게 표현해야할 필요가 있다.
자바와는 달리 C++의 경우에는 다중상속이 가능하다는 특성으로 인해 한단계 상위 클래스를 호출하는 참조자가 존재할 수가 없다.
자바는 다중상속이 불가능하므로 명시적으로 부모 클래스를 밝히지 않고도 부모 클래스의 멤버를 호출할 수 있다. 이와 같은 역할을 하는 것이
super라는 키워드이다.[4]8.3. 가상 함수 [편집]
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++에서는 가상 함수의 경우에만 동적 바인딩을 지원하며 상속이 가능하다.
9. 메모리 할당 [편집]
9.1. 정적 할당 [편집]
9.2. 자동 할당 [편집]
함수/메소드 안에서 값/객체를 생성하면 일반적으로 자동 메모리 할당이다. 예를 들면
에서 "
이 코드의 의도는 '0'을 가리키는 포인터를 반환시키는 함수를 만드는것이다. 일단 자동 할당으로
#include <iostream>
using namespace std;
int main() {
int x = 0;
cout << x << endl; //0
return 0;
}에서 "
int x = 0"가 컴퓨터 메모리를 정수에 할당하는것이다. 자동 할당은 컴퓨터의 메모리중 스택이라는 곳에 저장된다. 이렇게 만들어진 자료는 함수의 범위가 끝나면 할당된 메모리가 회수된다. 따라서 다음같은 코드는 C++에선 먹히지 않는다.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)이다.9.3. 동적 할당 [편집]
동적 할당은 스택 (stack) 대신 힙(heap)이란 메모리를 사용한다. 힙 영역에 메모리를 할당하려면 "
단 여기서 주의해야 할것이 있는데, 힙의 메모리를 자료에 할당해주면 이 자료는 써먹은 다음에 반드시 풀어주어야 한다. 그렇지 않으면 메모리 누수가 일어 나게 된다. "
동적 메모리 할당은 자동/정적 할당보다 주의가 요구 된다. 정수 몇개 처럼 적은 메모리 용량을 차지하는 자료라면 별 상관 없겠지만, 좀 더 방대한 자료를 다루는 프로그램이라면 필요없는 자료를 풀어주지 않았다가 맛이 가버리는 수가 있다.
그래서 현대(C++11 이상)에서는 소유권을 갖는 raw 포인터의 사용을 금지하도록 권고된다.
소유권 전달 시 반드시 스마트 포인터를 사용하자.
또한 서로 다른 C++ 런타임을 사용하는 코드가 있는 경우 해제(
GCC가 사용하는 libstdc++와 llvm이 사용하는 libc++, 그리고 MSVC가 사용하는 msvcp는 C 런타임과 다르게 서로 다른 ABI를 가지고 있으며 서로 할당 방법이 다르다. 예를 들어 libstdc++에서 할당한 포인터를 msvcp가 해제하는 경우 힙 커럽션등의 문제가 생길 수 있다.
마찬가지로 얼로케이터를 사용한 할당이나 아래의 libc 스타일의 malloc으로 할당한 포인터 또한 C++의
new" 표현식을 사용하면 된다. 동적 메모리 할당을 하려면 "T *ptr = new T"같은 식으로 쓰자. 예제로, 위에서 의도한 함수를 만들려면 다음과 같이 코드를 짜면 된다.int* zero_ptr(){
int *ptr = new int; //동적 할당
*ptr = 0;
return ptr;
}단 여기서 주의해야 할것이 있는데, 힙의 메모리를 자료에 할당해주면 이 자료는 써먹은 다음에 반드시 풀어주어야 한다. 그렇지 않으면 메모리 누수가 일어 나게 된다. "
new"로 동적 할당한 메모리는 "delete" 지시자로 풀어주자.#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 를 이용하여 이미 할당 된 메모리(스택도 포함)에 메모리를 할당 할 수도 있다 해당 방법으로 스택 메모리에다가 할당한 경우, 따로 해제를 해 주지 않아도 된다(다만, 클래스라면 소멸자는 호출 해 주어야 한다).9.3.1. C언어 방식 [편집]
C언어/문법 문서의 동적 할당 문단을 참조하면 된다.
10. 호출 방식 [편집]
후술할 예시의
increment 함수 선언에 따라 값에 의한 호출, 주소에 의한 호출, 참조에 의한 호출이 있다.10.1. 값에 의한 호출 [편집]
Pass(Call) by value
함수의 패러미터로 사용하기 위해 자료를 값에 의해 호출하는것. 여기서 중요한것은, 값에 의해 호출하면 해당 자료는 함수의 범위내에서 직접 사용되지 않고, 복사되어서 사용된다. 예를 들자면
함수의 패러미터로 사용하기 위해 자료를 값에 의해 호출하는것. 여기서 중요한것은, 값에 의해 호출하면 해당 자료는 함수의 범위내에서 직접 사용되지 않고, 복사되어서 사용된다. 예를 들자면
#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에는 아무런 영향을 주지 않는다.10.2. 주소에 의한 호출 [편집]
Pass(Call) by address(pointer)
자료의 값이 아닌 주소 (포인터)[5]를 호출. 실제
값에 의한 호출과는 다르게 주소에 의한 호출은 포인터를 사용함으로써 오리지날 자료를 복사하지 않는다. 큰 자료를 함수의 패러미터로 사용할 생각이라면 복사하느라 메모리 낭비를 안 해도 되니 괜찮은 수법이다.
자료의 값이 아닌 주소 (포인터)[5]를 호출. 실제
void increment 함수를 만들 수 있는 방법 중 하나다.#include <iostream>
void increment (int *x){ //주소에 의한 호출
++(*x);
}
int main(){
int a = 0;
increment(&a);
std::cout << a; //1
return 0;
}값에 의한 호출과는 다르게 주소에 의한 호출은 포인터를 사용함으로써 오리지날 자료를 복사하지 않는다. 큰 자료를 함수의 패러미터로 사용할 생각이라면 복사하느라 메모리 낭비를 안 해도 되니 괜찮은 수법이다.
10.3. 참조에 의한 호출 [편집]
Pass(Call) by reference.
참조에 의해 호출을 하면 패러미터가 함수 범위내에서 복제되지 않고 원본이 그대로 사용된다.
void increment 같은 함수를 만들 수 있는 또 하나의 방법이다.#include <iostream>
void increment (int &x){ //참조에 의한 호출.
++x; //여기서 x는 복제체가 아님.
}
int main(){
int a = 0;
increment(a);
std::cout << a; //1
return 0;
}참조에 의해 호출을 하면 패러미터가 함수 범위내에서 복제되지 않고 원본이 그대로 사용된다.
[1] 네임스페이스 멤버로는 클래스, 클래스 멤버 함수가 있다.[2] 비주얼 스튜디오에서는 C언어 및 C++ 소스코드에
*.cpp 확장자를 기본으로, G++에서는 *.cc 확장자를 기본으로 사용한다. 애초에 확장자는 프로그래머가 구별하기 쉽게 하기 위함이지 땩히 표준은 없다.[3] 부모 클래스인 MyParent 클래스에서 멤버 변수 text를 protected 권한으로 선언하고, public 상속을 했기 때문에 접근이 가능하다. 자세한 내용은 상속 방식에서 설명한다.[4] 다만, super.super처럼 super를 두번 이상 사용하는 것은 자바에서 문법적으로 허용되지 않는다. 자바에서 두단계 이상 상위 클래스를 호출하고 싶다면 C++처럼 클래스 이름을 직접적으로 명시하여 호출해야 한다. 상위 클래스 이름.멤버 메소드 이름(매개 변수) 이런 식으로 호출해야 한다.[5] 밑의 예시에서는 a의 메모리 주소를 가르키는 x 포인터를 사용했다라이선스를 별도로 명시하지 않은 문서는 CC BY-NC-SA 2.0 KR에 따라 이용할 수 있습니다.
기여하신 문서의 저작권은 각 기여자에게 있으며, 각 기여자는 기여하신 부분의 저작권을 갖습니다.
문서의 기여자는 역사 탭에서 확인할 수 있습니다.
접두어의 N: - 나무위키 사용자, R: - 리그베다 위키의 사용자를 뜻합니다.
자세한 사항은 나무위키에서 동일한 문서의 역사를 참고하시기 바랍니다.