2015-11-08 36 views
4

好了,它会一点一点技巧。这里是(简化)代码:呼唤的基地之一赋值操作符与C++多重继承的派生类的虚函数表

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 
} 

对我来说,这件事情在“只是避免做这样”线,但这是一个现有的代码,我们正在怀疑这个代码附近有可疑的内存损坏。所以,我很好奇,如果没关系。

好奇的部分这里是两个基类有虚函数表和他们都没有任何明确的复制/分配构造/运营商。

从我的理解,对于一个Derived类的内存布局如下

`Derived` 
--------- 
A-vtable 
A-fields 
B-vtable 
B-fields 

,当我调用虚函数,在“B”,宣布我使用B-vtable,当我打电话一个虚拟函数,在“A”中声明,我使用的是A-vtable,即vtables没有合并在一起。

而且从我的理解隐含的复制/赋值构造函数/运营商乙方应只影响B-fields,当它被称为*static_cast<B*>(this) = b;static_cast应该给一个指针开始的B-fields,与B-vtable居住在负从中偏移,据我所知) 。

所以,从我的理解,这个代码是完全安全的,正确的,虽然不清楚,哈克,但安全。我对么?是否有任何编译器特定的怪癖我应该知道(我们在这里讨论MSVC 2012)?

编辑:伙计们,我知道复制构造函数/赋值运算符的区别,非常感谢。这是发生在3次复制构造函数中的事件之一,因为我监督了它,现在每个答案都会花费一半的文本来告诉完全不相关的问题区别。

+0

“经历了一个微妙的内存损坏可疑接近这个代码” - 去提取小例子呢! –

+0

指针如'this'通常指向隐藏字段,其中V表指针被存储(即,对象的开始,不与偏移到它),因为它是用来相当频繁。指针是否来自静态投射是无关紧要的。我认为它也可能是负偏移,这可能是一个实现细节。 –

回答

1

是的,这是正确的。有一种派生类派生到其父母的特殊行为。当发生多重继承时,就像你的情况一样,指针的实际地址可以改变。若要使用exampe:

this      -> | A-vtable | 
           | A-fields | 
static_cast<B*>(this) -> | B-vtable | 
           | B-fields | 

该指针的变化一样,当你调用从B一个Derived对象导出的函数发生。


但是,要知道,拷贝构造函数是你在该行援引什么:

*static_cast<B*>(this) = b; 

相反,要调用的B赋值运算符,即B::operator=(const B& other)。如果您没有定义一个,则使用默认赋值运算符。等号仅在变量声明的情况下被视为一个拷贝构造函数:

B newObj = b; 

如果你想实现自己的拷贝构造函数为Derived,然后显式调用的B父拷贝构造函数,它试一下:

Derived::Derived(const B& b) : B(b) 
{ 
} 
1

为什么不直接调用基类的构造函数?

Derived::Derived(const B& b) 
: B(b) 
{ 
} 

类似的事情可以赋值运算符来完成:

Deriver& Derived::operator=(const Derived& rhs) 
{ 
    A::operator=(rhs); //Assign A's part 
    B::operator=(rhs); //Assign B's part 
    //Derived-specific assignments 

    return *this; 
} 

此操作的首选,安全和完全有效的方法来初始化类的对象,从继承的东西。

而且,在这一行:

*static_cast<B*>(this) = b; 

你叫赋值运算符,而不是拷贝构造函数。既然你写了,既没有定义基本类型,也使用了default(由编译器生成)。

我不明白为什么你甚至想到vtable。这是使用编译器生成的拷贝构造函数和赋值操作符的类的简单继承。一切都很简单,可以没有任何黑客:)做

+0

这不是我的代码,我通常完全避免继承,抛开多重继承。我知道还有其他方法可以在没有黑客的情况下做到这一点,但问题不在于此。问题是关于 - 这种方式是否安全。但是,无论如何,我想知道是否打电话给一个完全合格的运营商也会这样做。 –

相关问题