C ++ 11使我们能够创建具有非平凡成员的匿名联合。有时这可能非常有用-例如,如果我想为一些不带默认值的普通对象创建Holder类。
通过为其提供虚拟方法,使该NonTrivial对象更加有趣:
#include <stdint.h>
#include <stdio.h>
struct Base
{
virtual void something() { printf("something\n"); }
};
struct NonTrivial : Base
{
explicit NonTrivial( int ) : a(1), b(2), c(3), d(4) { printf("NonTrivial\n"); }
virtual void something() override { printf("something non trivial\n"); }
int a;
int b;
int c;
int d;
};
struct Holder
{
Holder() : isNonTrivial(false), dummy(0x77) {}
Holder( NonTrivial n) : isNonTrivial(true), nonTrivial( n ) {}
bool isNonTrivial;
union
{
int dummy;
NonTrivial nonTrivial;
};
Holder & operator=( const Holder & rhs )
{
isNonTrivial = rhs.isNonTrivial;
if( isNonTrivial )
nonTrivial = rhs.nonTrivial;
return *this;
}
};
int main() {
Holder holder_1;
NonTrivial n(1);
Holder holder_2( n );
holder_1 = holder_2;
holder_2.nonTrivial.something();
holder_1.nonTrivial.something();
return 0;
}
这是可行的。但是,这仅适用于 ,因为编译器实际上并未在此处进行虚拟调用。让我们强迫它:
Base * ptr = &holder_1.nonTrivial;
ptr->something();
这会产生段错误。
但为什么?我或多或少做了明显的事情-检查holder是否持有一个非平凡的对象,如果是,则复制它。
阅读完程序集后,我发现此operator=
实际上并没有从rhs.nonTrivial复制vtable指针。我认为发生这种情况是因为{NonTrivial}的operator=
仅应在完全构造的对象上调用,而完全构造的对象应该已经初始化了其vtable指针-那么为什么还要打扰并复制它呢?
问题:
operator=
应该看起来如何
创建nonTrivial对象的完整副本?我有两个想法-删除
完全operator=
并强迫用户使用复制控制器-或使用
全新放置,而不是nonTrivial = rhs.nonTrivial
-但也许
还有其他选择吗?P.S。我知道std :: optional之类的东西,我想自己了解如何做。
答案 0 :(得分:1)
如果有人偶然发现这个问题以寻求快速解答,这就是我使用新的刊登位置解决此问题的方法:
template< typename T, typename ... Args >
void inplace_new( T & obj, Args && ... args )
{
auto * t = &obj;
t = new(t) T{ args... };
}
Holder & operator=( const Holder & rhs )
{
isNonTrivial = rhs.isNonTrivial;
if( isNonTrivial )
inplace_new( nonTrivial, rhs.nonTrivial );
return *this;
}
别忘了#include <new>
:)