사람들은 "C++은 배치를 삭제할 필요가 없기 때문에 아무 것도하지 않을 것"이라고 말한 것으로 들었습니다.삭제 표현에 관해서는 C++에서 "placement delete"가 없다는 것
는 다음과 같은 코드를 생각해 보면 소멸자가 가상되고, 모두 제대로이라고 여기에서 볼 수 있듯이
#include <cstdlib>
#include <cstdio>
#include <new>
////////////////////////////////////////////////////////////////
template<typename T, typename... ARGS>
T* customNew1(ARGS&&... args) {
printf("customNew1...\n");
auto ret = new T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
template<typename T>
void customDelete1(T *ptr) {
printf("customDelete1...\n");
delete ptr;
printf("OK\n\n");
}
////////////////////////////////
template<typename T, typename... ARGS>
T* customNew2(ARGS&&... args) {
printf("customNew2 alloc...\n");
void *buf = std::malloc(sizeof(T));
printf("customNew2 construct...\n");
auto ret = ::new(buf) T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
template<typename T>
void customDelete2(T *ptr) {
printf("customDelete2 destruct...\n");
// what I want: a "placement delete" which calls the destructor and returns the address that should be passed to the deallocation function
// e.g.
//
// void* ptrToFree = ::delete(ptr);
// std::free(ptrToFree);
//
// equally fine would be a "magic" operator that allows one to obtain said address without actually calling the destructor:
//
// void* ptrToFree = get_deallocation_address_of(ptr);
// ptr->~T();
// std::free(ptrToFree);
ptr->~T();
printf("customDelete2 free...\n");
std::free(ptr);
printf("OK\n\n");
}
////////////////////////////////////////////////////////////////
struct A {
int a;
A() : a(0) {
printf("A()\n");
}
virtual ~A() {
printf("~A()\n");
}
};
struct B {
int b;
B() : b(0) {
printf("B()\n");
}
virtual ~B() {
printf("~B()\n");
}
};
struct C : A, B {
int c;
C() : c(0) {
printf("C()\n");
}
~C() {
printf("~C()\n");
}
};
////////////////////////////////////////////////////////////////
int main() {
C *c1 = customNew1<C>();
A *a1 = c1;
B *b1 = c1;
// Assume c and a will be the same but b is offset
printf("c: %x\n", c1);
printf("a: %x\n", a1);
printf("b: %x\n", b1);
printf("\n");
customDelete1(b1); // <- this will work, the delete expression offsets b1 before deallocing
printf("--------------\n\n");
C *c2 = customNew2<C>();
A *a2 = c2;
B *b2 = c2;
printf("c: %x\n", c2);
printf("a: %x\n", a2);
printf("b: %x\n", b2);
printf("\n");
// customDelete2(b2); // <- this will break
customDelete2(a2); // <- this will work because a2 happens to point at the same address as c2
printf("--------------\n\n");
return 0;
}
을하지만, B2의 해제는 여전히 C2가 아닌 다른 주소에서 B2 점 때문에 실패합니다. Global "placement" delete[]
그러나이 큰 문제없이 해결할 수있다 단순히에 배열 크기를 저장하여 여기 설명 된 바와 같이 하나의 새로운 [] 오브젝트의 배열을 구성하는 배치를 사용하는 경우와 유사한 문제가 발생
참고 메모리 블럭의 헤드이며 단일 객체 배치 새로운/명시 적 소멸자 호출을 사용하여 배열 생성자/소멸자 호출을 루프에서 수동으로 처리합니다.
한편, 다중 상속 문제를 해결할 수있는 유익한 방법은 생각할 수 없습니다. delete 표현식 내의 기본 포인터에서 원래 포인터를 가져 오는 "magic"코드는 구현에 따라 다르며 배열과 마찬가지로 "수동으로 수행"하는 간단한 방법은 없습니다.
#include <cstdlib>
#include <cstdio>
#include <new>
////////////////////////////////////////////////////////////////
// imagine this is a library in which all allocations/deallocations must be handled by this base interface
class Alloc {
public:
virtual void* alloc(std::size_t sz) =0;
virtual void free(void *ptr) =0;
};
// here is version which uses the normal allocation functions
class NormalAlloc : public Alloc {
public:
void* alloc(std::size_t sz) override final {
return std::malloc(sz);
}
void free(void *ptr) override final {
std::free(ptr);
}
};
// imagine we have a bunch of other versions like this that use different allocation schemes/memory heaps/etc.
class SuperEfficientAlloc : public Alloc {
void* alloc(std::size_t sz) override final {
// some routine for allocating super efficient memory...
(void)sz;
return nullptr;
}
void free(void *ptr) override final {
// some routine for freeing super efficient memory...
(void)ptr;
}
};
// etc...
////////////////////////////////
// in this library we will never call new or delete, instead we will always use the below functions
// this is used instead of new...
template<typename T, typename... ARGS>
T* customNew(Alloc &alloc, ARGS&&... args) {
printf("customNew alloc...\n");
void *buf = alloc.alloc(sizeof(T));
printf("customNew construct...\n");
auto ret = ::new(buf) T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
// um...
thread_local Alloc *stupidHack = nullptr;
// unfortunately we also have to replace the global delete in order for this hack to work
void operator delete(void *ptr) {
if (stupidHack) {
// the ptr that gets passed here is pointing at the right spot thanks to the delete expression below
// alloc has been stored in "stupidHack" since it can't be passed as an argument...
printf("customDelete free @ %x...\n", ptr);
stupidHack->free(ptr);
stupidHack = nullptr;
} else {
// well fug :-D
}
}
// ...and this is used instead of delete
template<typename T>
void customDelete(Alloc &alloc, T *ptr) {
printf("customDelete destruct @ %x...\n", ptr);
// set this here so we can use it in operator delete above
stupidHack = &alloc;
// this calls the destructor and offsets the pointer to the right spot to be dealloc'd
delete ptr;
printf("OK\n\n");
}
////////////////////////////////////////////////////////////////
struct A {
int a;
A() : a(0) {
printf("A()\n");
}
virtual ~A() {
printf("~A()\n");
}
};
struct B {
int b;
B() : b(0) {
printf("B()\n");
}
virtual ~B() {
printf("~B()\n");
}
};
struct C : A, B {
int c;
C() : c(0) {
printf("C()\n");
}
~C() {
printf("~C()\n");
}
};
////////////////////////////////////////////////////////////////
int main() {
NormalAlloc alloc;
C *c = customNew<C>(alloc);
A *a = c;
B *b = c;
printf("c: %x\n", c);
printf("a: %x\n", a);
printf("b: %x\n", b);
printf("\n");
// now it works
customDelete(alloc, b);
printf("--------------\n\n");
return 0;
}
이 질문 내가 어떤 것을 매우 확신하지 않기 때문에 단지 호언 장담 정말 더되지 않은 : 여기
이 그것을 해결하기 위해 추한 해킹, 문제가되고 또 다른 상황은 마법 연산자 또는 플랫폼 독립적 인 방법으로 주소를 얻습니다. 내가 일하는 회사에서 글로벌 new/delete를 대체 할 필요가있는 다른 프로그램과 정적으로 링크해야만 할 때까지 위의 해킹으로 사용자 정의 할당자를 사용하는 라이브러리를 만들었습니다. 우리의 현재 솔루션은 단순히 가장 파생 된 객체와 같은 주소를 가질 수없는 기본에 대한 포인터를 통해 객체를 삭제하는 것을 금지하지만 이는 약간 불행한 것으로 보입니다. "ptr-> ~ T(); 자유 (ptr);" 일반적으로 충분한 패턴 인 것처럼 보이고 많은 사람들이 삭제 표현과 같다고 생각하는 것 같지만 그렇지 않습니다. 다른 사람들이이 문제를 겪고 어떻게 해결했는지 궁금합니다.
'dynamic_cast '은 마술 같은 연산자입니다. –
간단한 힌트, 그런 것들이 필요하다면 나는 scoped_ptr이나 shared_ptr 같은 객체를 커스텀 Deleter와 함께 사용한다. 이 객체를 사용하여 객체를 대기열 또는 사용자 정의 힙에 다시 배치 할 수 있지만 삭제/제거시 구체 유형을 사용하는 데 사용할 수도 있습니다. – Sven
이 경우 dynamic_cast은 제가 찾고있는 것입니다. 이것을 사용하는 것에 대해 모르는 것에 대해 멍청한 느낌. 불행히도 문제의 라이브러리는 사용하기 쉽도록 스마트 포인터와 사용자 정의 삭제자를 인식합니다. –
Anon49343283