C++ 删除此项并放置新的虚拟派生类 类基{ INTA; 受保护的: 模板 类派生; 公众: base(){} 虚拟~base(){} 虚拟void func(){} 静态基带*maker(); }; 模板 基类::派生类 :公共基地 { 公众: 派生(){} 虚~derived(){} 虚空函数(){ 这个->~derived();//
我认为这显然不好 作为前言,调用析构函数确实可以结束对象的生命周期,但您只允许(并且必须)在其位置构造相同类型的新对象:C++ 删除此项并放置新的虚拟派生类 类基{ INTA; 受保护的: 模板 类派生; 公众: base(){} 虚拟~base(){} 虚拟void func(){} 静态基带*maker(); }; 模板 基类::派生类 :公共基地 { 公众: 派生(){} 虚~derived(){} 虚空函数(){ 这个->~derived();//,c++,templates,virtual,placement-new,C++,Templates,Virtual,Placement New,我认为这显然不好 作为前言,调用析构函数确实可以结束对象的生命周期,但您只允许(并且必须)在其位置构造相同类型的新对象: class base { int a; protected: template<class T> class derived; public: base() {} virtual ~base() {} virtual void func() {} static base* maker(); }; templ
class base {
int a;
protected:
template<class T>
class derived;
public:
base() {}
virtual ~base() {}
virtual void func() {}
static base* maker();
};
template <class T>
class base::derived
: public base
{
public:
derived() {}
virtual ~derived() {}
virtual void func() {
this->~derived(); //<--is this legal?
new (this) derived<int>(); //<--is this legal?
}
};
base* base::maker() {
return new derived<double>();
}
int main() {
base* p = base::maker(); //p is derivedA<double>
p->func(); //p is now derivedA<int>
delete p; //is the compiler allowed to call ~derived<double>()?
}
请记住,在作用域的末尾将调用析构函数
但在你的例子中,你正在构建一个完全不同的对象:
::new(&x)Bar;//Bar=DerivedA
显然,如果sizeof(Bar)
超过sizeof(Foo)
这是不正常的
(如果您可以对对象大小以及对齐保证做出额外保证,我们可以进一步考虑。)
更新:即使你在想,好吧,这些类型都是从同一个基派生的,所以调用析构函数会带来虚拟的快乐,我仍然很确定这是一种违反。在这个静态设置中,编译器可能会静态地解析虚拟调用,因此如果你更改&x
指向的对象
更新2:关于同一问题的另一个想法是:
*&x
的静态类型是已知的,我认为您必须尊重这一点。换句话说,编译器没有理由考虑局部变量的静态类型发生变化的可能性。我很确定这是无效的代码,原因有几个:
<强>编辑:< /强>当我查看新的C++标准时,我发现[Basy.Leave](Pr.3.85)给出了与未定义行为的例子基本相同的东西(它实际上并没有破坏对象,但我看不出这能使事情变得更好):
#包括
结构{
虚空f();
空变();
虚拟~B();
};
结构D1:B{void f();};
结构D2:B{void f();};
void B::mutate(){
new(this)D2;//重用存储结束*this的生命周期
f();//未定义的行为
…=this;//好的,这指向有效内存
}
void g(){
void*p=std::malloc(sizeof(D1)+sizeof(D2));
B*pb=新的(p)D1;
pb->mutate();
&pb;//确定:pb指向有效内存
void*q=pb;//确定:pb指向有效内存
pb->f();//未定义的行为,*pb的寿命已结束
}
这应该证明这是不允许的。不管它是否正确。头脑正常的人都不会这样做。即使它确实有效,也将是维护的噩梦,因此在实际代码中永远不会发生。离题:为什么要转换到怪物指针?Placement new需要一个空指针。也许您的案例是一个候选者对于动态强制转换为无效!
void*p=dynamic\u cast(this);new(p)T;
我很确定,如果用于堆栈上声明的变量,这将严重损坏。@KerrekSB我是否感觉到您试图从本周早些时候的一个问题中学到的东西中获得乐趣?:)@MatteoItalia:不,这完全可以(见我的答案).w00te:有一分钟,我以为我们可能在这里发现了一些东西,但现在我确信这毫无意义:-(在我的OP中,注意到它们保证大小相同。在您的更新中:这在符合要求的编译器中是允许的吗?我是违反规范还是编译器?(假设它不工作。我还没有实际测试。)@MooingDuck:如果你的例子是字面意思,那么你不需要任何虚拟层次结构或任何析构函数,所以你可能很好。我的答案是针对一个更一般的情况,我猜。我重新阅读了Matteo Italia,意识到他在我的SSCCE中发现了潜在的错误,而这些错误不在我的真实代码中,所以我更新了这个问题澄清不能在堆栈上创建派生的。但我认为这不会影响您的答案。在实际代码中,基包含一个缓冲区,派生的正在将t
放入其中,并且虚拟成员对该t
进行操作。需要虚拟层次结构。@moing:如果您有不管怎样,为什么在基类中有一个缓冲区,而不是在de中有一个正确类型的值
{
Foo x;
x.~Foo();
::new (&x) Foo;
} // x.~Foo() must be a valid call here!
::new (&x) Bar; // Bar = DerivedA<int>
#include<cstdlib>
structB{
virtual void f();
void mutate();
virtual ~B();
};
struct D1:B { void f(); };
struct D2:B { void f(); };
void B::mutate(){
new(this)D2; //reuses storage—ends the lifetime of *this
f(); //undefined behavior
...=this; //OK, this points to valid memory
}
void g(){
void* p = std::malloc(sizeof(D1) + sizeof(D2));
B* pb = new(p)D1;
pb->mutate();
&pb; //OK: pb points to valid memory
void* q = pb; //OK: pb points to valid memory
pb->f(); //undefined behavior, lifetime of *pb hasended
}