【问题标题】:How to copy object which contains a pointer to one of several other members?如何复制包含指向其他几个成员之一的指针的对象?
【发布时间】:2018-08-21 05:42:42
【问题描述】:

考虑一下:

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);
}

这里的“基”地址可以是 _a 以外的其他地址,例如 &amp;_obj(然后将差异应用于 this)。

有没有更好/更清洁的选择?

【问题讨论】:

  • 出于好奇,你想用这个模式来完成什么?
  • 除了“安全吗?”问“它可读吗?”和“它可维护吗?”
  • coliru.stacked-crooked.com/a/649b11b1b76c977c 您可以使用指向成员函数的指针来简化复制分配。其他一切都变得复杂,但是...
  • 我不知道如何用指向成员变量的指针做同样的事情,因为它不喜欢变量具有不同的类型,因为来自Derived Obj::* 的转换`Base Obj::* 是不允许的,因为可以尝试使用它来编辑原始数据。因此,我不得不求助于通用的 getter 函数。
  • 为什么不使用 std::variant?

标签: c++ copy-constructor


【解决方案1】:

要计算_current,您应该将obj._current&amp;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 风格的演员表:

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));
}

【讨论】:

  • 没有char 超过一个字节的平台。 char根据定义是一个字节。一个字节可能是 43 位,但 char 同样是 43 位。
  • @MooingDuck 现代字节总是 8 位。它记录在 ISO/IEC 2382-1:1993 中。不要混合字节和字。 Char 根据定义不是一个字节。 char是sizeof(char)为1的类型。char的定义中没有字节。
  • @S.M. C++ 中没有“现代字节”的概念。 C++ 标准只说字节不能小于 8 位。 stackoverflow.com/questions/5516044/system-where-1-byte-8-bit
  • @liliscent 好的,实际字节而不是现代字节听起来会更好。再一次,C++ 说 char 不能小于 8 位,CHAR_BIT &gt;= 8,而不是 byte。
  • @S.M. sizeof 运算符在其操作数的对象表示中产生 字节数expr.sizeof
【解决方案2】:

Ad hoc 多态可以在没有脆弱的指针算法的情况下完成。

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 };
}

【讨论】:

  • 投掷cost char(&amp;)[5] 看起来很残忍
  • @MoonDuck 复仇。
猜你喜欢
  • 2011-05-17
  • 2021-04-12
  • 1970-01-01
  • 1970-01-01
  • 2018-05-30
  • 2015-08-07
  • 2016-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多