可以在调用构造函数之前完成赋值吗?
Posted
技术标签:
【中文标题】可以在调用构造函数之前完成赋值吗?【英文标题】:Can assignment be done before constructor is called? 【发布时间】:2009-06-17 21:46:09 【问题描述】:对What's wrong with this fix for double checked locking? 的评论说:
问题是变量可能是 在构造函数运行之前分配 (或完成),而不是在对象之前 已分配。
让我们考虑代码:
A *a;
void Test()
a = new A;
为了进行更正式的分析,让我们将 a = new A 拆分为几个操作:
void *mem = malloc(sizeof(A)); // Allocation
new(mem) A; // Constructor
a = reinterpret_cast<A *>(mem); // Assignment
上面引用的评论是否属实,如果属实,在什么意义上?构造函数可以在赋值之后执行吗?如果可以,由于 MT 安全需要保证订单时,可以采取什么措施?
【问题讨论】:
没有什么可以对付它,真的。您将必须使任何读取和任何写入变得易失,并且必须使“内存”也变得易失,以保持代码写入的顺序,并让所有这些写入/读取由序列点分隔。但这仍然不会对多线程做任何事情:标准对此一无所知。 如“int volatile a, b; a = 1; b = 2;”这两个不能重新排序,并且在“int volatile a; int b; a = 1; b = 2;”中那么这些 can 可以重新排序,因为 b 不是易变的。而在 f(a=1, b=2);然后这些也可以“重新排序”,因为一开始就没有任何顺序。我发现 alexandrescu 和 scott meyers 的文章很好地解释了这一点,恕我直言 见:常见的未定义行为***.com/questions/367633/… 【参考方案1】:问题不在于代码执行时,而更多地与写入顺序有关。
假设:
A()
member = 7;
然后:
singleton = new A()
这导致代码执行分配,写入内存(成员),然后写入另一个内存位置(单例)。一些 CPU 可以重新排序写入,以便在写入单例之后才能看到对成员的写入 - 本质上,在系统中其他 CPU 上运行的代码可以查看写入单例的内存,但成员是不是。
【讨论】:
【参考方案2】:我认为以下应该可行:
void Test()
A *temp = new A;
MemoryWriteBarrier(); // use whatever memory barrier your platform offers
a = temp;
【讨论】:
【参考方案3】:a
是一个具有静态存储持续时间的全局对象,因此它将在 main 主体执行之前的某个时间在一些预分配的存储中初始化。假设对 Test 的调用不是某些静态对象构造怪异的结果,a
将在调用 Test 时完全构造。
a = new A;
这个稍微不寻常的分配不会(仅)是标准的复制分配操作,因为您将指向A
的指针分配给a,而不是对象或引用。它是否真正编译以及它到底调用了什么取决于A
是否有一个赋值运算符,它接受一个指向A
的指针,或者可以从指向A
的指针隐式转换的东西,或者A
是否有一个非显式构造函数它接受一个指向A
的指针(或一个指向A
基类的指针)。
编辑后,您的代码做了一些不同的事情!
从概念上讲,它更像这样:
A *tmpa;
void *mem = ::operator new( sizeof(A) ); // ( or possibly A::operator new )
try
tmpa = new (mem) A; // placement new = default constructor call
catch (...)
::operator delete( mem );
throw;
a = tmpa; // pointer assignment won't throw.
写出这样的东西的危险在于,你隐式添加了许多原始序列中不存在的序列点,此外,编译器被允许生成看起来不像这样长的代码因为它的行为“好像”它是由执行程序可以确定的 this 编写的。这个“好像”规则仅适用于正在执行的线程,因为(当前)语言没有说明与其他线程的交互是否有效。
为此,您需要使用实现提供的特定行为保证(如果有)。
【讨论】:
您的回答是基于我的包含错字(缺少 *)的代码。抱歉,代码已修复。【参考方案4】:是的,可以在赋值后调用构造函数,尽管您给出的示例在内部不一致(正如评论所指出的那样)。
您可以装上一些锁以让您安心,但也很容易弄错。
见
“C++ 和双重检查锁定的风险”
Scott Meyers 和 Andrei Alexandrescu
http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
【讨论】:
以上是关于可以在调用构造函数之前完成赋值吗?的主要内容,如果未能解决你的问题,请参考以下文章