为C++中具有多个继承派生类的vtables的基之一调用赋值运算符
Calling an assignment operator for one of bases with vtables of multiple-inherited derived class in 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
}
对我来说,这是一种"避免这样做"的行为,但这是一个现有的代码,我们在这个代码附近经历了一个微妙的内存损坏,令人怀疑。所以,我很好奇这是否可以。
奇怪的是,这两个基类都有vtables,并且没有任何显式的复制/赋值构造函数/运算符。
根据我的理解,Derived
类的内存布局如下
`Derived`
---------
A-vtable
A-fields
B-vtable
B-fields
当我调用一个在"B"中声明的虚拟函数时,我使用的是B-vtable
,当我调用在"a"中宣布的虚拟函数,我使用A-vtable
,即vtables不会合并在一起。
根据我的理解,当B被调用为*static_cast<B*>(this) = b;
时,它的隐式复制/赋值构造函数/运算符应该只影响B-fields
(static_cast
应该给出一个指向B-fields
开头的指针,B-vtable
位于它的负偏移量AFAIK)。
因此,根据我的理解,这个代码是完全安全和正确的,虽然不清楚和黑客,但安全。我说得对吗?有什么编译器特有的怪癖我应该注意吗(我们在这里谈论的是MSVC 2012)?
编辑:伙计们,我知道复制构造函数/赋值运算符的区别,非常感谢。这是三次谈论复制构造函数的事件之一,因为我已经监督过它,现在每个答案都花了一半的时间来告诉完全没有问题的人。
是的,它是正确的。有一种特殊的行为,将派生类强制转换为其父类之一。当发生多重继承时,就像您的情况一样,指针的实际地址可能会发生变化。举个例子:
this -> | A-vtable |
| A-fields |
static_cast<B*>(this) -> | B-vtable |
| B-fields |
当您在Derived
对象上调用从B
派生的函数时,也会发生同样的指针变化。
但是,请注意,复制构造函数是而不是您在以下行中调用的:
*static_cast<B*>(this) = b;
相反,您调用的是B
的赋值运算符,即B::operator=(const B& other)
。如果没有定义,则使用默认的赋值运算符。等号仅在变量声明的上下文中被视为复制构造函数:
B newObj = b;
如果您想为Derived
实现自己的复制构造函数,然后显式调用B
的父复制构造函数,请尝试以下操作:
Derived::Derived(const B& b) : B(b)
{
}
为什么不直接调用基类的构造函数?
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;
您调用赋值运算符,而不是复制构造函数。由于您编写了,两个基类型都没有定义,因此使用默认值(由编译器生成)。
我不明白你为什么还要考虑vtables。这是从具有编译器生成的复制构造函数和赋值运算符的类的简单继承。一切都很简单,无需任何技巧即可完成:)
- 重载Singly Linked List中的赋值运算符
- 使用赋值运算符重载从类中返回jobject
- 标准库类型的赋值运算符的引用限定符
- 复制构造函数、赋值运算符C++
- 标准::变体的赋值运算符
- 移动赋值运算符;尝试引用已删除的函数.我该如何解决这个问题?
- 基类和派生类的多态赋值运算符
- 为用户定义的类正确调用复制构造函数/赋值运算符
- CRTP 中的复制赋值运算符 - gcc vs clang 和 msvc
- 为什么初始化时没有调用重载赋值运算符?
- 赋值运算符重载和自赋值
- C++矢量复制构造函数和赋值运算符是否也复制保留空间?
- Qt PL/SQL - 赋值运算符 - 字符串缓冲区太小
- 对 r 值使用移动赋值运算符时的异常
- 由于没有使用赋值运算符,映射的值是如何初始化的?
- 默认赋值运算符如何在实际 STL 中实现
- 使用赋值运算符复制 std::vector
- C++ 通过自定义赋值运算符隐式转换函数参数
- 赋值运算符的奇怪行为
- 为C++中具有多个继承派生类的vtables的基之一调用赋值运算符