如何复制包含指向其他几个成员之一的指针的对象?

How to copy object which contains a pointer to one of several other members?

考虑一下:

class Base {};    
class A : public Base {};
class B : public Base {};
class C : public Base {};
class D : public Base {};

class Obj
{
public:
    Obj(const Obj&);

private:
    A _a;
    B _b;
    C _c;
    D _d;
    Base *_current;
};

_current 始终指向 _a_b_c_d 之一。 ABCD 可以有不同的大小。我想实现 Obj(const Obj&) 以便副本的 _current 本身指向适当的成员。

这种方法安全吗:

Obj::Obj(const Obj& obj) :
    _a {obj._a},
    _b {obj._b},
    _c {obj._c},
    _d {obj._d}
{
    auto objAAddr = reinterpret_cast<const char *>(&obj._a);
    auto objCurAddr = reinterpret_cast<const char *>(obj._current);
    auto diff = objCurAddr - objAAddr;
    auto myAAddr = reinterpret_cast<char *>(&_a);
    _current = reinterpret_cast<Base *>(myAAddr + diff);
}

"base" 地址可能不同于此处的 _a,例如 &_obj(然后将差异应用于 this)。

是否有 better/cleaner 替代方案?

要计算 _current,您应该将 obj._current&obj 之间的差值添加到 this。要正确计算偏移量,您应该使用特殊类型 uintptr_tchar * 不适合,因为有特定平台 char 超过一个字节。

#include <cstdint>
Obj::Obj(const Obj& obj) :
    _a {obj._a},
    _b {obj._b},
    _c {obj._c},
    _d {obj._d}
{
    _current = reinterpret_cast<Base *>(reinterpret_cast<uintptr_t>(this) +
        (reinterpret_cast<const uintptr_t>(obj._current) - 
            reinterpret_cast<const uintptr_t>(&obj)));
}

我会在那里使用 C-style cast:

Obj::Obj(const Obj& obj) :
    _a {obj._a},
    _b {obj._b},
    _c {obj._c},
    _d {obj._d}
{
    _current = (Base *)((uintptr_t)this +
        ((const uintptr_t)obj._current - (const uintptr_t)&obj));
}

即席多态性无需脆弱的指针运算即可实现。

class Base {};    
class A : public Base {};
class B : public Base {};
class C : public Base {};
class D : public Base {};

class Obj
{
public:
    Obj(const Obj&);
    Obj() { set_current(aa);  }
private:
    A _a;
    B _b;
    C _c;
    D _d;
    Base *_current;
    enum base_tag {
        aa, bb, cc, dd
    } tag;
    void set_current(base_tag t) {
        tag = t;
        switch (tag) {
        case aa: _current = &_a; break;
        case bb: _current = &_b; break;
        case cc: _current = &_c; break;
        case dd: _current = &_d; break;
        default: throw("gulp");
        }
    }
};

Obj::Obj(const Obj& obj) :
    _a {obj._a},
    _b {obj._b},
    _c {obj._c},
    _d {obj._d}
{
    set_current(obj.tag);
}

int main() {
    Obj o1;
    Obj o2{ o1 };
}