如何复制包含指向其他几个成员之一的指针的对象?
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
之一。 A
、B
、C
和 D
可以有不同的大小。我想实现 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_t
。 char *
不适合,因为有特定平台 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 };
}
考虑一下:
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
之一。 A
、B
、C
和 D
可以有不同的大小。我想实现 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_t
。 char *
不适合,因为有特定平台 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 };
}