배치 구문
Placement syntaxC++ 프로그래밍 언어에서는 배치 구문을 사용하여 프로그래머가 개개의 오브젝트, 즉 메모리 내의 "배치"의 메모리 관리를 명시적으로 지정할 수 있습니다.일반적으로 개체가 동적으로 생성되면 할당 함수는 개체에 메모리를 할당하고 새로 할당된 메모리 내에서 개체를 초기화하도록 호출됩니다.배치 구문을 사용하면 프로그래머는 할당 함수에 추가 인수를 제공할 수 있습니다.일반적인 용도는 오브젝트를 초기화할 수 있는 적절한 스토리지 영역에 포인터를 제공하여 메모리 할당을 오브젝트 [citation needed]구축에서 분리하는 것입니다.
의 "배치" 버전은new
그리고.delete
연산자와 함수를 배치라고 합니다.new
및 배치delete
A.[1]new
expression, placement 또는 그 외의 방법으로 호출한다.new
할당자 함수라고도 하며 이름은operator new
마찬가지로 a.delete
표현은 A를 호출한다.delete
디할로케이터 함수라고도 하며 이름은operator delete
를 클릭합니다.[2][3]
조금도new
배치 구문을 사용하는 표현식은 배치입니다.new
expression 및 anyoperator new
또는operator delete
필수 첫 번째 매개 변수보다 많은 값을 사용하는 함수(std::size_t
)는 배치 신규 또는 배치 삭제 기능입니다.[4]배치 새 함수는 다음 두 가지 입력 파라미터를 사용합니다.std::size_t
그리고.void *
.
역사
이전 버전의 C++에서는 placement new와 같은 것은 없었습니다.대신 개발자들은 유사한 [5]효과를 얻기 위해 컨스트럭터 내에서 이것에 대한 명시적인 할당을 사용했습니다.이 관행은 나중에 폐지되고 폐지되었으며 C++ Programming Language 제3판에서는 이 기술에 대해 언급하지 않았습니다.
표현.
비배치용 표준 C++ 구문new
표현은[2]
new new-type-id ( optional-initializer-expression-list )
placement 구문은 다음 명령어 바로 뒤에 식 목록을 추가합니다.new
키워드를 지정합니다.이 식 리스트가 배치입니다.임의의 수의 [2][3][6]식을 포함할 수 있습니다.
new ( expression-list ) new-type-id ( optional-initializer-expression-list )
기능들
배치 새 함수는 배치되지 않은 새 함수의 오버로드입니다.비배치 및 배열에 대한 배치되지 않은 새 함수 선언new
표현은 다음과 같습니다.[7][8]
무효* 교환입니다. 신규(표준::size_t) 던지다(표준::bad_flashed(불량_flashing)); 무효* 교환입니다. 신규[](표준::size_t) 던지다(표준::bad_flashed(불량_flashing));
Standard C++ 라이브러리는 이러한 기능에 대해 각각2개의 배치 오버로드를 제공합니다.선언은 다음과 같습니다.[7][8]
무효* 교환입니다. 신규(표준::size_t, 컨스턴트 표준::하지 않다&) 던지다(); 무효* 교환입니다. 신규(표준::size_t, 무효*) 던지다(); 무효* 교환입니다. 신규[](표준::size_t, 컨스턴트 표준::하지 않다&) 던지다(); 무효* 교환입니다. 신규[](표준::size_t, 무효*) 던지다();
모든 오버로드에서 첫 번째 파라미터는operator new
함수는 유형입니다.std::size_t
이 함수가 호출되면 할당하는 메모리의 양(바이트 단위)을 지정하는 인수로 전달됩니다.모든 함수가 유형을 반환해야 합니다.void *
이것은 함수가 [2]할당하는 스토리지에 대한 포인터입니다.
배치 삭제 기능도 있습니다.배치되지 않은 삭제 함수의 오버로드 버전입니다.배치되지 않은 삭제 함수는 다음과 [7][8]같이 선언됩니다.
무효 교환입니다. 삭제하다(무효*) 던지다(); 무효 교환입니다. 삭제하다[](무효*) 던지다();
Standard C++ 라이브러리는 이러한 기능에 대해 각각2개의 배치 오버로드를 제공합니다.선언은 다음과 같습니다.[7][8]
무효 교환입니다. 삭제하다(무효*, 컨스턴트 표준::하지 않다&) 던지다(); 무효 교환입니다. 삭제하다(무효*, 무효*) 던지다(); 무효 교환입니다. 삭제하다[](무효*, 컨스턴트 표준::하지 않다&) 던지다(); 무효 교환입니다. 삭제하다[](무효*, 무효*) 던지다();
모든 오버로드에서 첫 번째 파라미터는operator delete
함수는 유형입니다.void *
할당 [2]해제할 스토리지의 주소입니다.
new 함수 및 delete 함수 모두 함수는 글로벌하고 네임스페이스에 존재하지 않으며 정적 링크도 [2]없습니다.
사용하다
배치 구문에는 기본 배치, 예외 방지, 사용자 지정 할당자 및 디버깅의 4가지 주요 용도가 있습니다.
디폴트 배치
의 배치 과부하operator new
그리고.operator delete
추가 고용을 하는 것void *
파라미터는 포인터 배치라고도 하는 기본 배치에 사용됩니다.C++ 프로그램이 대체 또는 재정의하는 것은 허용되지 않는 표준 C++ 라이브러리에 의한 정의는 다음과 같습니다.[7][8][9]
무효* 교환입니다. 신규(표준::size_t, 무효* p) 던지다() { 돌아가다 p; } 무효* 교환입니다. 신규[](표준::size_t, 무효* p) 던지다() { 돌아가다 p; } 무효 교환입니다. 삭제하다(무효*, 무효*) 던지다() { } 무효 교환입니다. 삭제하다[](무효*, 무효*) 던지다() { }
디폴트 배치에는 다양한 용도가 있습니다.
Bjarne Strostrup은 그의 저서 The Design and Evolution of C++에서 포인터 배치 new가 특정 하드웨어 주소에서 특정 객체를 예상하는 하드웨어에 필요하다고 처음 관찰했습니다.멀티프로세서 컴퓨터의 [10]여러 프로세서 간에 공유되는 영역 등 특정 메모리 영역에 상주할 필요가 있는 오브젝트 구축에도 필요합니다.
그러나 다른 용도로는 C++ 언어에서는 [3]허용되지 않는 컨스트럭터를 직접 호출하는 것이 있습니다.
C++언어는 프로그램이 디스트럭터를 직접 호출할 수 있도록 합니다.또한 C++언어를 사용하여 오브젝트를 파기할 수 없기 때문에delete
expression, 이것이 포인터 배치 새 expression을 통해 생성된 개체를 파괴하는 방법입니다.예를 [11][12]들어 다음과 같습니다.
p->~T();
사용 사례
placement new는 오퍼레이터가 메모리를 새로 할당하는 것을 원하지 않지만(이미 할당되어 있고 개체를 배치하는 경우), 개체를 구성하려는 경우 사용됩니다.이것이 필요한 일반적인 상황의 예는 다음과 같습니다.
- 서로 다른 두 프로세스 간에 공유되는 메모리 내의 개체를 생성하려고 합니다.
- 개체를 페이지 불가능한 메모리에 생성하려고 합니다.
- 예를 들어 std::벡터<>를 실장할 때 메모리 할당을 구조로부터 분리할 필요가 있습니다(std::벡터<>:reserve 참조).
기본적인 문제는 컨스트럭터가 특수한 함수라는 것입니다.시작할 때 오브젝트는 없고 원시 메모리만 존재합니다.이 작업이 완료되면 완전히 초기화된 개체가 생성됩니다.따라서 i) 컨스트럭터를 오브젝트 ii로 호출할 수 없습니다.단, 비정적 멤버에 액세스(초기화)해야 합니다.이로 인해 컨스트럭터를 직접 호출하는 것은 오류가 됩니다.해결책은 연산자 new의 배치 형태입니다.
이 연산자는 다음과 같이 구현됩니다.
무효* 교환입니다. 신규(표준::size_t 세어보세요, 무효* 여기서) { 돌아가다 여기서; } 무효* 교환입니다. 신규[](표준::size_t 세어보세요, 무효* 여기서) { 돌아가다 여기서; }
예외 방지
일반적으로 (배치되지 않은) 새로운 함수는 다음과 같은 유형의 예외를 발생시킵니다.std::bad_alloc
사용 가능한 메모리가 모두 소진되는 등의 오류가 발생한 경우.이것은 Strustrup의 주석 첨부 C++ 참조 매뉴얼에서 함수를 정의하는 방식이 아니라 C++ 언어가 표준화되었을 때 표준화 위원회가 변경한 것입니다.함수의 원래 동작은 다음과 같습니다.NULL
에러가 발생했을 때의 포인터는 배치 [3][4][6]구문을 통해 액세스 할 수 있습니다.
프로그램에서 이 작업을 수행하려면 표준 C++ 라이브러리 헤더를 포함해야 합니다.<new>
소스코드에 있습니다.이 헤더는 글로벌을 선언합니다.std::nothrow
오브젝트, 유형std::nothrow_t
(헤더에도 선언되어 있습니다).이것은, 오버로드 되고 있는 새로운 함수를 호출하기 위해서 사용됩니다.const std::nothrow_t &
두 번째 매개 변수로 사용합니다.예를 [9]들어 다음과 같습니다.
#실패하다 <신규> 구조 T {}; 인트 주된() { // 함수 연산자 new(std::size_t, const std::nothrow_t &)를 호출하여 (성공한 경우) 개체를 구성합니다. T* p = 신규 (표준::행하지 않다) T; 한다면 (p) { // 저장소가 할당되어 생성자가 호출되었습니다. 삭제하다 p; } 또 다른 ; // 오류가 발생했습니다.스토리지가 할당되지 않았고 개체가 구성되지 않았습니다. 돌아가다 0; }
커스텀 할당자
배치 구문은 사용자 지정 할당자에도 사용됩니다.표준 C++ 라이브러리 헤더에서 할당자 및 할당 해제 기능을 사용하지 않습니다.<new>
단, 프로그래머가 사용자 정의 유형에 대해 오버로드된 할당 및 할당 해제 함수를 직접 작성해야 합니다.예를 들어 다음과 [7][8]같이 메모리 관리 클래스를 정의할 수 있습니다.
#실패하다 <cstdlib> 학급 A { 일반의: 무효* 할당하다(표준::size_t); 무효 할당을 해제하다(무효*); };
커스텀 배치 할당 및 할당 해제 함수를 [7][8]다음과 같이 정의합니다.
무효* 교환입니다. 신규(표준::size_t 크기, A& 경기장) { 돌아가다 경기장.할당하다(크기); } 무효 교환입니다. 삭제하다(무효* p, A& 경기장) { 경기장.할당을 해제하다(p); }
프로그램은 배치 구문을 사용하여 다른 인스턴스를 사용하여 개체를 할당합니다.A
다음과 [7][8]같이 분류합니다.
A 첫 번째_마이너스탠다드, second_filename(세컨드_프론트); T* p1 = 신규(첫 번째_마이너스탠다드) T; T* p2 = 신규(second_filename(세컨드_프론트)) T;
이러한 방식으로 스토리지가 할당된 개체를 폐기하려면 주의가 필요합니다.배치 삭제 식이 없기 때문에 이 식을 사용하여 사용자 지정 할당 해제기를 호출할 수 없습니다.커스텀 디할로케이터를 호출하는 파괴 함수를 쓰거나 배치 삭제 함수를 함수 [11][7][8]호출로 직접 호출해야 합니다.
전자는 [8]다음과 같습니다.
무효 파괴하다(T* p, A& 경기장) { p->~T(); // 먼저 소멸자를 명시적으로 호출합니다. 경기장.할당을 해제하다(p); // 그런 다음 디할로케이터 함수를 직접 호출합니다. }
이 명령어는 프로그램에서 다음과 같이 호출됩니다.
A 경기장; T* p = 신규(경기장) T; /* ... */ 파괴하다(p, 경기장);
후자는 파괴자 호출과 삭제 함수 호출을 프로그램에 [7][13]단순히 쓰는 것을 포함합니다.
A 경기장; T* p = 신규(경기장) T; /* ... */ p->~T(); // 먼저 소멸자를 명시적으로 호출합니다. 교환입니다. 삭제하다(p, 경기장); // 그런 다음 연산자 삭제(void*, A &)를 통해 간접적으로 할당 해제 함수를 호출합니다.
일반적인 오류는 삭제 식을 사용하여 개체를 삭제하려고 시도하는 것입니다.이것은 잘못된 결과를 낳는다.operator delete
호출된 함수입니다.Dewhurst는 이 오류를 피하기 위해 두 가지 방법을 권장합니다.첫 번째는 모든 커스텀 할당자가 Standard C++ 라이브러리의 글로벌한 비배치에 의존하도록 하는 것입니다.operator new
따라서 C++ 라이브러리의 메모리 관리를 둘러싼 단순한 래퍼에 지나지 않습니다.두 번째는 개개의 클래스에 대해 새로운 함수를 만들고 삭제하고 배치 [13]구문을 사용하는 것이 아니라 클래스 함수 멤버를 통해 메모리 관리를 맞춤화하는 것입니다.
디버깅
Placement new는 메모리 할당에 실패한 소스 코드의 파일 이름과 행 번호를 프로그램이 인쇄할 수 있도록 하는 간단한 디버깅 도구로도 사용할 수 있습니다.표준 C++ 라이브러리 헤더를 포함할 필요가 없습니다.<new>
단, 4개의 배치 함수와 매크로 치환을 선언하는 헤더를 포함해야 합니다.new
새로운 표현에서 사용되는 키워드입니다.예를 들어, 이러한 헤더에는 다음이 포함됩니다.[9][14]
#정의되어있는경우(DEBUG_NEW) 무효* 교환입니다. 신규(표준::size_t 크기, 컨스턴트 차* 파일, 인트 선); 무효* 교환입니다. 신규[](표준::size_t 크기, 컨스턴트 차* 파일, 인트 선); 무효 교환입니다. 삭제하다(무효* p, 컨스턴트 차* 파일, 인트 선); 무효 교환입니다. 삭제하다[](무효* p, 컨스턴트 차* 파일, 인트 선); #새로운 정의(_FILE__, __LINE__) #실패하다 #정의신규 #엔디프
이것은 다음과 [9][14]같은 프로그램에서 사용됩니다.
T* p = 신규 T;
커스텀 작성 배치의 새로운 함수는 예외 발생 시 제공된 파일 및 회선 번호 정보를 사용하여 처리합니다.예를 [9][14]들어 다음과 같습니다.
#실패하다 <신규> #실패하다 <cstdlib> 학급 새 오류 { 일반의: 새 오류(컨스턴트 차* 파일, 인트 선) { /* ... */ } /* ... */ } ; 무효 * 교환입니다. 신규(표준::size_t 크기, 컨스턴트 차* 파일, 인트 선) { 한다면 (무효* p = ::교환입니다. 신규(크기, 표준::행하지 않다)) 돌아가다 p; 던지다 새 오류(파일, 선); }
배치 삭제
위에서 설명한 바와 같이 배치 삭제 표현은 없습니다.배치를 호출할 수 없습니다.operator delete
사용하여 기능하다delete
표현.[11][15]
배치 삭제 함수가 배치에서 호출됩니다.new
표현.특히 오브젝트 생성자가 예외를 발생시키면 호출됩니다.이 때, 프로그램이 메모리 리크를 일으키지 않게 하기 위해서, 배치 삭제 기능을 호출한다.배치 새 표현식이 먼저 배치를 호출합니다.operator new
이 함수는 할당자 함수에서 반환된 원시 스토리지에서 개체 생성자를 호출합니다.컨스트럭터가 예외를 슬로우하는 경우 배치 새 식을 실행한 코드로 예외를 전파하기 전에 해당 스토리지의 할당을 해제해야 합니다.그것이 배치 삭제 [2][4][11][15]함수의 목적입니다.
호출되는 배치 삭제 함수는 배치 새 식에 의해 호출된 배치 새 함수와 일치합니다.따라서 예를 들어 다음 코드가 실행되면 호출되는 배치 삭제 함수는operator delete(void *, const A &)
다음과 같습니다.[2][11][15]
#실패하다 <cstdlib> #실패하다 <iostream> 구조 A {}; 구조 E {}; 학급 T { 일반의: T() { 던지다 E(); } }; 무효 * 교환입니다. 신규(표준::size_t, 컨스턴트 A&) { 표준::외치다 << > "새로운 위치 호출" << > 표준::끝; } 무효 교환입니다. 삭제하다(무효*, 컨스턴트 A&) { 표준::외치다 << > "배치 삭제가 호출되었습니다." << > 표준::끝; } 인트 주된(){ A a; 해라 { T* p = 신규(a) T; } 또 만나 (E exp) { 표준::외치다 << > "예외 포착" << > 표준::끝; } 돌아가다 0; }
이것이 포인터 배치 삭제 함수가 표준 C++ 라이브러리에 의해 no-operations로 정의되는 이유입니다.포인터 배치의 새 함수는 스토리지를 할당하지 않으므로 개체 생성자가 [11]예외를 발생시킬 경우 스토리지를 할당 해제할 수 없습니다.
일치하는 배치 삭제 함수가 없는 경우 배치 내에서 생성자에 의해 예외가 발생해도 할당 해제 함수가 호출되지 않습니다.new
표현.배치 삭제를 전혀 지원하지 않는 (오래된) 일부 C++ 구현도 있습니다(예외 슬로우 할당자 함수처럼 표준화 시 C++에 추가된 것입니다.두 경우 모두 커스텀할로케이터를 사용하여 할당할 때 컨스트럭터에 의해 예외가 발생하면 메모리 누수가 발생합니다.(오래된 C++ 실장의 경우 메모리 누수는 배치되지 않은 경우에도 발생합니다.new
표현)[4][15]
보안.
새 식을 배치하면 보안 공격에 취약합니다.2011년 Kundu와 Bertino는[16] 새로운 배치에 대한 몇 가지 이점을 시연했습니다.공격에는 버퍼 오버플로 공격, 오브젝트 오버플로, 선택적 스택 가드 덮어쓰기, 가상 포인터 서브터퍼지, 메모리 정렬 오류 공격 등이 있습니다.2015년에 GCC는 [16]의 조사 결과를 바탕으로 패치를[17] 발표했습니다.
메모들
- ^ 맥클러스키 2000
- ^ a b c d e f g h 리스네르 2003, 페이지 72-73, 128-129, 310, 623-625
- ^ a b c d 리프먼 1997, 386-389페이지
- ^ a b c d 마이어스 1998
- ^ 스트루스트럽 1991[페이지 필요]
- ^ a b 루돈 2003, 페이지 109–110
- ^ a b c d e f g h i j Vermeir 2001, 113–115페이지
- ^ a b c d e f g h i j Strustrup 1997, 페이지 255~256, 576
- ^ a b c d e 앤더슨 1998a, 345-356페이지
- ^ 스트루스트럽 1994, 페이지 214
- ^ a b c d e f Solter & Kleper 2005, 페이지 458-461
- ^ 시드 & 쿠퍼 2001, 435–436페이지
- ^ a b 듀허스트 2003, 페이지 173–176
- ^ a b c 용웨이 2007
- ^ a b c d 앤더슨 1998b, 631–632페이지
- ^ a b c Kundu, Ashish; Bertino, Elisa (June 2011). "A New Class of Buffer Overflow Attacks". 2011 31st International Conference on Distributed Computing Systems: 730–739. doi:10.1109/ICDCS.2011.63 – via IEEE.
- ^ "Martin Sebor - [PING] [PATCH] c++/67942 - diagnose placement new buffer overflow". gcc.gnu.org. Retrieved 2020-06-15.
레퍼런스
- Anderson, Gail (1998a). "Object Storage Management". Navigating C++ and Object-oriented Design. Prentice Hall. ISBN 9780135327487.
- Anderson, Gail (1998b). "Exception Handling". Navigating C++ and Object-oriented Design. Prentice Hall. ISBN 9780135327487.
- Buck, Joe (1997-05-12). "3.4. g++ won't accept the placement new syntax". Frequently asked questions about the GNU C++ compiler. Retrieved 2008-11-26.
- Dewhurst, Stephen C. (2003). "Gotcha #62: Replacing Global New and Delete". C++ Gotchas. Addison-Wesley. ISBN 978-0-321-12518-7.
- Lischner, Ray (2003). C++ in a Nutshell. O'Reilly. ISBN 9780596002985.
- Lippman, Stanley B. (1997). C++ Gems. Cambridge University Press. ISBN 9780135705810.
- Loudon, Kyle (2003). C++ Pocket Reference. O'Reilly. ISBN 9780596004965.
- "Placement New/Delete". C++ Language and Library. Glen McCluskey & Associates LLC. 2000-06-26. Archived from the original on 2006-04-18. Retrieved 2008-11-26.
- Meyers, Scott (1998-04-01). "Placement new and placement delete". Dr. Dobb's Journal. United Business Media LLC.
- Seed, Graham M.; Cooper, Barry J. (2001). An Introduction to Object-oriented Programming in C++ (Second ed.). Springer. ISBN 1-85233-450-9.
- Solter, Nicholas; Kleper, Scott (2005). Professional C++. Wiley. ISBN 9780764574849.
- Stroustrup, Bjarne (July 1991). The C++ Programming Language (2nd ed.). ISBN 978-0-201-53992-9.
- Stroustrup, Bjarne (1994). "Memory Management". Design and Evolution of C++. Addison-Wesley. ISBN 978-0-201-54330-8.
- Stroustrup, Bjarne (1997). The C++ Programming Language (3rd ed.). Addison-Wesley. ISBN 978-0-201-88954-3.
- Vermeir, Dirk (2001). Multi-paradigm Programming Using C++. Springer. ISBN 9781852334833.
- Yongwei, Wu (2007-12-31). "A Cross-Platform Memory Leak Detector". Wu Yongwei's Programming Page. Retrieved 2008-11-26.
추가 정보
- Franek, Frantisek (2004). Memory as a Programming Concept in C and C++. Cambridge University Press. ISBN 978-0-521-52043-0.
- Cline, Marshall (2006-09-25). "11.10: What is "placement new" and why would I use it?". C++ FAQ Lite. Retrieved 2008-11-26.
- "C++ new Operator". IBM's Mac OS X compilers. IBM. 2003. Retrieved 2008-11-27.
- "The operator new Function". MSDN. Microsoft. Retrieved 2008-11-27.
- "new Operator (C++)". MSDN. Microsoft. Retrieved 2008-11-27.