C ++就地构造&后续破坏:我如何获得正确的指针?

时间:2015-08-22 23:27:16

标签: c++ pointers constructor specifications

免责声明:这是一个细致入微的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_castMyClass*转换为void*并且然后回到MyClass*

还有一个提及(C ++ 2003:3.8.5)与此相关,指定在调用析构函数之后但在释放内存之前,可以执行一组非常具体的操作。那个指针。出于我们的目的,我们关心的是它包括将其转换为void*(这是有效的,因为旧指针“指向有效的内存”)。

没有说明void*是否是用于就地构造的原始void*

所以:如何从结果指针中获取用于就地构造的原始指针

3 个答案:

答案 0 :(得分:3)

您可以使用dynamic_cast<void*>从基类中检索原始指针。生成的指针始终指向派生程度最高的类,a.k.a。整个对象。

据我所知,pStoragep是不同的地址是违法的。

答案 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分配的对象的显式管理类,这绝对不是一个好主意。