免责声明:这是一个细致入微的C ++问题,与严格阅读C ++规范有很大关系。
在C ++中,使用就地构造的传统方法是:
void * pStorage = myPooledAllocationFunction ( /*pool number*/1 ) ; // allocate from pool number 1, for example
MyClass * p = new (pStorage) MyClass () ;
但重要的是要注意reinterpret_cast<void*> ( p )
不一定等于pStorage
。
旁注:当我刚刚读到这个位置时,我感到很沮丧,因为它只是说“请用这个额外的参数调用operator new
”以及使用它的传统方式恰好在C ++中有一个默认实现。
我相信解除分配这个新创建的对象的传统方法是:
p->~MyClass() ;
myPoolDeallocationFunction ( /*pool number*/1 ) ; // deallocate pool 1 in its entirety
这假设您能够做到这一点。但是如果你不是呢?如果您需要从p
获取原始指针怎么办?
这有效吗?
p->~MyClass() ;
myPooledDeallocationFunction ( static_cast<void*> ( p ) ) ;
C ++规范(C ++ 2003:5.2.9.10)指定使用static_cast
从MyClass*
转换为void*
并且然后回到MyClass*
。
还有一个提及(C ++ 2003:3.8.5)与此相关,指定在调用析构函数之后但在释放内存之前,可以执行一组非常具体的操作。那个指针。出于我们的目的,我们关心的是它包括将其转换为void*
(这是有效的,因为旧指针“指向有效的内存”)。
没有说明void*
是否是用于就地构造的原始void*
。
所以:如何从结果指针中获取用于就地构造的原始指针?
答案 0 :(得分:3)
您可以使用dynamic_cast<void*>
从基类中检索原始指针。生成的指针始终指向派生程度最高的类,a.k.a。整个对象。
据我所知,pStorage
和p
是不同的地址是违法的。
答案 1 :(得分:2)
听起来这是一个需要教学答案的学术问题。
但是,如果你愿意接受蛮力和无知,那么就可以达到你的目的,即使在两个边界的边界上也可以将你的池块从原始内存中分割出来,从而导致块被保证更大比任何要放置的物体(并且可能是正确对齐的)。
(然后屏蔽p的相应低位以获得原始新目标。)
答案 2 :(得分:1)
大多数编译器都使用了一个非常简单的placement new实现,正如“C ++编程语言”中的11.2.4所指出的那样。
例如VS - 2013:
#ifndef __PLACEMENT_NEW_INLINE
#define __PLACEMENT_NEW_INLINE
inline void *__CRTDECL operator new(size_t, void *_Where) _THROW0()
{ // construct array with placement at _Where
return (_Where);
}
但是,如果放置new的返回必须与传递的指针相同,那么本书并不是显式地解释。
所以,AFAIK,它应该工作。而@Puppy指向的dynamic_cast<void*>
检索原始指针看起来很棒。
但是,我想谈谈显式的析构函数调用,以及Stroustrup所说的内容:
“请注意,除了资源的实现之外,应该避免显式调用析构函数 管理类。“
我认为,因为在破坏时你没有原始存储指针,因为它不是在资源管理类中执行的。
通常我认为在C ++编程中找到那些tarpits或问题与某些设计缺陷有关(当然我并不质疑你所做的任何设计),但也许它们应该被用作思考的信号我们究竟在做什么。
在这个具体的例子中,混合使用放置新对象和常规对象分配的对象而没有为使用placement new分配的对象的显式管理类,这绝对不是一个好主意。