【问题标题】:Copy part of a class复制课程的一部分
【发布时间】:2012-02-25 23:27:09
【问题描述】:

我想将类的一部分复制到缓冲区。我需要这个来制作一些可以查看类中是否有更改的东西,并通过网络发送它以更新服务器上的更改。

我做了一个模板类,可以备份和恢复类。现在我正在制作“寻找差异”功能。我希望用户可以定义内存块的大小。这会将类分成几部分,发送的数据更少。

问题是我无法将类内存的一部分复制到堆中,我无法获得正确的地址。如果我做“班级地址”+“0x04”。然后我没有得到正确的地址。

这是我做的一个例子:

testclass test1;
testclass test2;

test1.set(1,1);
test2.set(1,2);

cout << &test1 << " " << (&test1 + 0x04) << endl; //0018FF24 0018FF44

memcpy(&test2,&test1 + 0x04,4);

test2.echo(); //Wrong data!

标题:

class testclass
{
    int test,test2;

public:
    void set(int a, int b) {test = a, test2 = b;}
    void echo() {cout << test << " " << test2 << endl;}
};

希望有人能帮我解决这个问题。

谢谢!

【问题讨论】:

    标签: c++ class heap-memory memcpy


    【解决方案1】:

    基本上,你不能乱用这样的指针。您通常不能依赖编译器来巧合地将有意义的数据放在那里。

    如果你想要成员的地址,你应该写 &amp;(test1.test2) 而不是 &amp;test1+0x04 因为即使 IF 一个 int 是 4 个字节并且 IF 编译器没有填充结构和如果你或其他人没有改变类的内容,那么&amp;test1+0x04 真正的意思是“&amp;test1 加上4*sizeof(test) 字节”,这是到达(&amp;test1)[4] 的另一种方式就指针数组等价而言。

    此外,您一般不能memcpy 超过类并期待有意义的结果,除非它们是 POD。

    如果你想比较一个类的实例,你应该写一个函数来依次比较每个成员。

    您不能为此编写通用方法,因为 C++ 不是反射语言。这意味着您不能编写神奇地知道类成员的名称和类型的代码。

    因此,如果您想像这样比较和修补数据,则需要执行以下操作:

    struct Foo {
        int a;
        int b;
    
        void export_differences (std :: ostream & o, const Foo & f) {
            if (a != f.a) o << "a " << f.a << " ";
            if (b != f.b) o << "b " << f.b << " ";
            o << ";";
        }
    
        void import_differences (std :: istream & i) {
            std :: string s;
            while (i >> s) {
                if (s == "a") i >> a;
                else if (s == "b") i >> b;
                else if (s == ";") break;
                else throw std :: runtime_error ("bad input");
            }
        }
    };
    

    您必须为每个要修补的类编写类似的内容。

    【讨论】:

    • 不仅如此,你不知道类成员变量中包含的数据是否是指向更多数据的指针。
    • 感谢您的帮助。我认为一般来说需要一种方法来做到这一点。我要做一个导入和导出功能。谢谢!
    • 您可以使用 Javascript 和某些其他语言。微软和其他公司已经尝试过具有反射性的 C++ 实验方言,但开销令人望而却步。
    【解决方案2】:

    神奇的0x04和4从何而来?

    如果这行得通,那只是因为特定的对齐和实现。 更好的结构化方式:

    class testclass
    {
        int test,test2;
    
    public:
        void set(int a, int b) {test = a, test2 = b;}
        void echo() {cout << test << " " << test2 << endl;}
    
        void backup_to(testclass& s)
        { s.test2 = test2; }
    
        bool has_changed_respect(const testclass& s)
        { return s.test2 == test2; }
    
        friend std::ostream& operator<<(std::ostream& s, const testclass& a)
        { return s << "testclass["<<&a<<"]: test="<<a.test<<", test2="<<a.test2<< std::endl; }
    };
    
    int main()
    {
        testclass t1, t2;
        t1.set(1,1);
        t2.set(3,4);
    
        std::cout << t1 << t2;
    
        t1.backup_to(t2);
        std::cout << t2;
    
        t1.set(5,6);
        cout << t1 << t2 << t1.is_changed_respect(t2) << std::endl; 
    
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      &amp;test1 + 0x04 表示将testclass 的大小增加4 倍到test1 的地址,得到的指针类型为testclass*。它将指向数组的第五个元素,其第一个元素的地址为test1,但test1 不是数组的一部分,因此添加行为未定义。

      您似乎想要在test1 的地址上添加 4 个字节。例如,您可以使用((char*)&amp;test1) + 4 执行此操作,这会产生char* 类型的指针。请注意,该标准不保证 sizeof(int) 为 4,也不保证 offsetof(testclass, test2) == sizeof(int)

      您可以使用char*unsigned char* 来检查任何对象的内存。但是这种能力的用处有一些限制:

      1. 类中可以有填充字节,可以采用任何值。因此,仅仅因为两个对象在内存中具有不同的字节值并不意味着它们不相等。
      2. 非 POD 类中可以有很多任意的“额外内容”,由实现放置在那里,并且不能安全地按字节复制。
      3. 具有指针成员的类通常不能安全地按字节复制,当然也不能通过网络复制到另一台机器。
      4. 即使类是 POD,如果您通过网络发送它,那么您必须确保该类在两台机器上具有相同的布局。如果代码是使用不同的选项、由不同的编译器或针对不同的架构编译的,则不一定会出现这种情况,但有时确实如此。

      【讨论】:

        【解决方案4】:

        1) 首先,你需要你的班级是POD。至少你可以取出数据成员来分离结构。

        2) 然后,如果根据需要选择偏移粒度 = 1、2、4 或 2^n 字节;并确定适合此要求的类型:2^n == sizeof(chunk_type)。例如,您希望进行逐字节比较,因此将指向 _State(或 MyClass 参见 #1)的指针转换为所需类型:(char*)this->m_state。

        这是一个函数,它试图找到两个类中不同的第一个块,如果没有发现差异,则返回其偏移量或 -1。

        class MyClass {
          struct _State { 
            int a,b
          };
        
          _State m_state;
        
        public:
          typedef char  chunk_type;
          int next_diff_pos(const MyClass& other, size_t offset = 0) const {
              chunk_type *pThis = &m_state, 
                         *pOther = &other.m_state;
        
              if (offset < sizeof(_State)) {
                size_t n = offset;
                while(*pThis++ == *pOther++ && n < sizeof(_State)) 
                  n++;
                // We need to test this again, because of ambigous while condition
                if (n < sizeof(_State))
                  return n;
              } 
        
              return -1;
          }
        };
        

        PS:当然,您的 chunk_type 必须定义 == 运算符(对于 char、int 和其他标量已完成此操作)。

        (我没有测试过代码)

        【讨论】:

          猜你喜欢
          • 2022-01-25
          • 2013-06-13
          • 2012-03-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多