【发布时间】:2015-11-08 11:11:48
【问题描述】:
好的,这会有点棘手。这是一个(简化的)代码:
class A
{
virtual ~A();
// fields, none of which has an assignment operator or copy constructor
};
class B
{
virtual ~B();
// same as A
};
class Derived : public A, public B
{
Derived();
Derived(const B& b);
// no fields
};
使用Derived::Derived(const B& b)(即接受它的基础之一)如下
Derived::Derived(const B& b)
{
*static_cast<B*>(this) = b;
// Do other stuff with protected fields declared in B
}
对我来说,这符合“避免这样做”,但这是一个现有的代码,我们在此代码附近遇到了可疑的细微内存损坏。所以,我很好奇这样可以吗。
这里奇怪的是,两个基类都有 vtables,它们都没有任何显式的复制/赋值构造函数/操作符。
据我了解,Derived 类的内存布局如下
`Derived`
---------
A-vtable
A-fields
B-vtable
B-fields
当我调用在“B”中声明的虚函数时,我使用的是B-vtable,当我调用在“A”中声明的虚函数时,我使用的是A-vtable,即vtables没有合并在一起。
据我了解,B 的隐式复制/赋值构造函数/运算符应该只影响B-fields,当它被称为*static_cast<B*>(this) = b; 时(static_cast 应该给出指向B-fields 开头的指针,B-vtable 驻留与它的负偏移量,AFAIK)。
所以,根据我的理解,这段代码是完全安全和正确的,虽然不清楚和hacky,但安全。我对么?是否有任何我应该注意的特定于编译器的怪癖(我们在这里谈论的是 MSVC 2012)?
编辑:伙计们,我知道复制构造函数/赋值运算符的区别,非常感谢。这是仅讨论复制构造函数的 3 次事件之一,因为我已经监督了它,现在每个答案都花费一半的文本来说明与问题差异完全无关的内容。
【问题讨论】:
-
“在这段代码附近出现了可疑的内存损坏” - 那就去提取一个最小的例子吧!
-
像
this这样的指针通常指向存储vtable指针的隐藏字段(即指向对象的最开始,而不是指向它的偏移量),因为它经常使用。指针是否来自静态转换无关紧要。我认为它也可能是负偏移,这可能是一个实现细节。