다음 코드가 있다고 가정합니다.
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
안전한가요? 아니면 삭제 ptr
하기 char*
전에 캐스팅 해야 합니까?
다음 코드가 있다고 가정합니다.
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
안전한가요? 아니면 삭제 ptr
하기 char*
전에 캐스팅 해야 합니까?
답변:
그것은 "안전"에 달려 있습니다. 할당 자체에 대한 포인터와 함께 정보가 저장되기 때문에 일반적으로 작동하므로 할당 해제자가 올바른 위치로 반환 할 수 있습니다. 이러한 의미에서 할당자가 내부 경계 태그를 사용하는 한 "안전"합니다. (많은 사람들 이요)
그러나 다른 답변에서 언급했듯이 void 포인터를 삭제하면 소멸자가 호출되지 않아 문제가 될 수 있습니다. 그런 의미에서 "안전"하지 않습니다.
당신이하고있는 일을 당신이하고있는 방식대로 할 이유가 없습니다. 자체 할당 해제 함수를 작성하려면 함수 템플릿을 사용하여 올바른 유형의 함수를 생성 할 수 있습니다. 이를 수행하는 좋은 이유는 특정 유형에 대해 매우 효율적일 수있는 풀 할당자를 생성하는 것입니다.
다른 답변에서 언급했듯이 이것은 C ++에서 정의되지 않은 동작 입니다. 일반적으로 주제 자체가 복잡하고 상반되는 의견으로 가득 차 있지만 정의되지 않은 동작을 피하는 것이 좋습니다.
sizeof(T*) == sizeof(U*)
모두 를 위한 사실 T,U
은 하나의 비 템플릿 void *
기반 가비지 수집기 구현 을 가질 수 있어야 함을 시사합니다 . 그러나 gc가 실제로 포인터를 삭제 / 해제해야 할 때 정확히이 질문이 발생합니다. 작동하게하려면 람다 함수 소멸자 래퍼 (urgh)가 필요하거나 유형과 저장 가능한 항목 사이를 오갈 수있는 일종의 동적 "데이터로서의 유형"유형이 필요합니다.
void 포인터를 통한 삭제는 C ++ 표준에 정의되어 있지 않습니다-섹션 5.3.5 / 3 참조 :
첫 번째 대안 (객체 삭제)에서 피연산자의 정적 유형이 동적 유형과 다른 경우 정적 유형은 피연산자의 동적 유형의 기본 클래스이고 정적 유형에는 가상 소멸자가 있거나 동작이 정의되지 않습니다. . 두 번째 대안 (배열 삭제)에서는 삭제할 객체의 동적 유형이 정적 유형과 다른 경우 동작이 정의되지 않습니다.
그리고 각주 :
이것은 void * 유형의 개체가 없기 때문에 void * 유형의 포인터를 사용하여 개체를 삭제할 수 없음을 의미합니다.
.
NULL
응용 프로그램 메모리 관리를 위해 차이를 만들어 보이드 포인터의 지적 주소 메모리를 채우 시겠습니까?
그것은 좋은 생각이 아니며 C ++에서 할 일이 아닙니다. 이유없이 유형 정보를 잃어 버리고 있습니다.
소멸자는 원시 유형이 아닌 유형에 대해 호출 할 때 삭제중인 배열의 객체에 대해 호출되지 않습니다.
대신 new / delete를 재정의해야합니다.
void *를 삭제하면 우연히 올바르게 메모리가 해제되지만 결과가 정의되지 않았기 때문에 잘못된 것입니다.
나에게 알려지지 않은 이유로 포인터를 void *에 저장하고 해제해야한다면 malloc과 free를 사용해야합니다.
질문은 말이되지 않습니다. 당신의 혼란은 부분적으로 사람들이 자주 사용하는 엉성한 언어 때문일 수 있습니다 delete
.
동적으로 할당 된 개체delete
를 삭제하는 데 사용 합니다 . 이렇게하면 해당 객체에 대한 포인터를 사용하여 삭제 표현식 을 형성 합니다 . "포인터를 삭제"하지 않습니다. 당신이 정말로하는 일은 "주소로 식별되는 객체를 삭제"하는 것입니다.
이제 왜 그 질문이 말이되지 않는지 알 수 있습니다. void 포인터는 "객체의 주소"가 아닙니다. 의미가없는 주소 일뿐입니다. 그것은 할 수있다 는 실제 개체의 주소에서왔다,하지만이로 인코딩했기 때문에 정보가 손실 유형 원래의 포인터. 객체 포인터를 복원하는 유일한 방법은 void 포인터를 객체 포인터로 다시 캐스팅하는 것입니다 (포인터가 의미하는 바를 작성자가 알아야 함). void
그 자체는 불완전한 유형이므로 객체의 유형이 아니므로 void 포인터를 사용하여 객체를 식별 할 수 없습니다. (객체는 유형과 주소로 공동으로 식별됩니다.)
delete
null 포인터 값, 이전 new-expression에 의해 생성 된 배열이 아닌 개체에 대한 포인터 또는 a에 대한 포인터 일 수 있습니다. 이러한 개체의 기본 클래스를 나타내는 하위 개체입니다. 그렇지 않으면 동작이 정의되지 않습니다. " 컴파일러는 진단, 그것의 아무것도하지만, 컴파일러의 버그없는 코드 ... 받아 그래서 만약
delete void_pointer
. 정의되지 않은 동작입니다. 프로그래머는 응답이 프로그래머가 원하는 작업을 수행하는 것처럼 보이더라도 정의되지 않은 동작을 호출해서는 안됩니다.
정말로 그렇게해야한다면, 중개자 ( new
및 delete
연산자)를 잘라 내고 글로벌 operator new
및 operator delete
직접 전화를 거는 것은 어떨까요? (물론 new
and delete
연산자 를 계측하려는 경우 실제로 operator new
and 를 다시 구현해야합니다 operator delete
.)
void* my_alloc (size_t size)
{
return ::operator new(size);
}
void my_free (void* ptr)
{
::operator delete(ptr);
}
달리 것을 참고 malloc()
, operator new
슬로우 std::bad_alloc
실패 (또는 호출 new_handler
등록되어있는 경우 하나).
char에는 특별한 소멸자 논리가 없기 때문입니다. 이것은 작동하지 않습니다.
class foo
{
~foo() { printf("huzza"); }
}
main()
{
foo * myFoo = new foo();
delete ((void*)foo);
}
감독은 전화를받지 않습니다.
void *를 사용하려면 malloc / free 만 사용하지 않는 이유는 무엇입니까? new / delete는 단순한 메모리 관리 그 이상입니다. 기본적으로 new / delete는 생성자 / 소멸자를 호출하며 더 많은 일이 진행됩니다. 내장 유형 (예 : char *)을 사용하고 void *를 통해 삭제하면 작동하지만 여전히 권장되지 않습니다. 결론은 void *를 사용하려는 경우 malloc / free를 사용하는 것입니다. 그렇지 않으면 편의를 위해 템플릿 기능을 사용할 수 있습니다.
template<typename T>
T* my_alloc (size_t size)
{
return new T [size];
}
template<typename T>
void my_free (T* ptr)
{
delete [] ptr;
}
int main(void)
{
char* pChar = my_alloc<char>(10);
my_free(pChar);
}
많은 사람들이 이미 무효 포인터를 삭제하는 것이 안전하지 않다고 언급했습니다. 나는 그것에 동의하지만 연속 배열이나 유사한 것을 할당하기 위해 void 포인터로 작업하는 경우 안전하게 new
사용할 수 있도록 이 작업을 수행 할 수 있음을 추가하고 싶습니다 delete
(with, ahem , 약간의 추가 작업). 이것은 메모리 영역 ( 'arena'라고 함)에 void 포인터를 할당 한 다음 아레나에 대한 포인터를 new에 제공함으로써 수행됩니다. C ++ FAQ 의이 섹션을 참조하십시오 . 이것은 C ++에서 메모리 풀을 구현하는 일반적인 접근 방식입니다.
이렇게 할 이유가 거의 없습니다.
우선, 당신은 모르는 경우 유형 의 데이터를, 그리고 당신이 알고있는 모든는 점입니다 void*
당신이 정말로 단지 유형이없는로 데이터를 처리해야한다, BLOB 이진 데이터 (의 unsigned char*
), 사용 malloc
/ free
그것을 다루는 . 이것은 때때로 void*
C api에 대한 포인터를 전달해야하는 파형 데이터 등과 같은 것에 필요합니다 . 괜찮아.
당신이 경우에 할 데이터의 유형 (즉, 그것의 ctor / dtor있다) 알고 있지만 어떤 이유로 당신은 함께 결국 void*
(어떤 이유 당신이) 포인터 당신이 정말로 유형에 다시 캐스팅해야 당신이 그것을 알고 수 및 통화 delete
그것에.
나는 코드 리플렉션과 다른 모호성의 업적 동안 프레임 워크에서 void * (알 수없는 유형)를 사용했으며 지금까지 컴파일러에서 문제 (메모리 누수, 액세스 위반 등)를 겪지 않았습니다. 비표준 작업으로 인한 경고 만 표시됩니다.
알려지지 않은 (void *)을 삭제하는 것은 완벽하게 합리적입니다. 포인터가 다음 지침을 따르는 지 확인하십시오. 그렇지 않으면 이해가되지 않을 수 있습니다.
1) 알 수없는 포인터는 사소한 해체자가있는 유형을 가리켜서는 안되므로 알 수없는 포인터로 캐스트 될 때 절대 삭제되지 않아야합니다. 알 수없는 포인터를 ORIGINAL 유형으로 다시 캐스팅 한 후에 만 삭제하십시오.
2) 인스턴스가 스택 바운드 또는 힙 바운드 메모리에서 알 수없는 포인터로 참조되고 있습니까? 알 수없는 포인터가 스택의 인스턴스를 참조하는 경우 절대 삭제해서는 안됩니다!
3) 알 수없는 포인터가 유효한 메모리 영역이라고 100 % 긍정적입니까? 아니요, 그러면 절대로 삭제해서는 안됩니다!
대체로 알 수없는 (void *) 포인터 유형을 사용하여 수행 할 수있는 직접적인 작업은 거의 없습니다. 그러나 간접적으로 void *는 C ++ 개발자가 데이터 모호성이 필요할 때 의지 할 수있는 훌륭한 자산입니다.
버퍼 만 원하면 malloc / free를 사용하십시오. new / delete를 사용해야하는 경우 간단한 래퍼 클래스를 고려하십시오.
template<int size_ > struct size_buffer {
char data_[ size_];
operator void*() { return (void*)&data_; }
};
typedef sized_buffer<100> OpaqueBuffer; // logical description of your sized buffer
OpaqueBuffer* ptr = new OpaqueBuffer();
delete ptr;
char의 특별한 경우.
char은 특수 소멸자가없는 내장 유형입니다. 따라서 유출 주장은 논쟁의 여지가 있습니다.
sizeof (char)는 일반적으로 하나이므로 정렬 인수도 없습니다. sizeof (char)가 1이 아닌 드문 플랫폼의 경우 char에 맞게 정렬 된 메모리를 할당합니다. 따라서 정렬 논쟁도 논쟁의 여지가 있습니다.
이 경우 malloc / free가 더 빠릅니다. 그러나 std :: bad_alloc을 상실하고 malloc의 결과를 확인해야합니다. 글로벌 new 및 delete 연산자를 호출하는 것이 중간 사람을 우회하므로 더 나을 수 있습니다.
new
실제로 던지기로 정의 되었다고 생각합니다 . 이것은 사실이 아닙니다. 컴파일러 및 컴파일러 스위치에 따라 다릅니다. 예를 들어 MSVC2019 /GX[-] enable C++ EH (same as /EHsc)
스위치를 참조하십시오 . 또한 임베디드 시스템에서 많은 사람들이 C ++ 예외에 대한 성능 세를 지불하지 않기로 선택합니다. 따라서 "But you forfeit std :: bad_alloc ..."로 시작하는 문장은 의심 스럽습니다.