复制类的一部分

发布于 2025-01-02 11:58:53 字数 756 浏览 0 评论 0原文

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

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

问题是我无法将类内存的一部分复制到堆中,我无法获得正确的地址。如果我做“类的地址”+“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;}
};

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

谢谢!

I want to copy a part of a class to a buffer. I need this to make something that can look if there are changes in a class, and send it over the network to update the changes on the server.

I made a template class that can back-up and restore classes. Now I am making the "look for differences" function. I want that the user can define how big the memory blocks will be. This split the class in parts and takes less data to send.

The problem is that I can't copy a part of the class memory to the heap, I can't get the address correct. If I do "address of the class" + "0x04". Then I don't get the correct addres.

This is an exaple that I made:

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!

The header:

class testclass
{
    int test,test2;

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

I hope someone help me with this problem.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不一样的天空 2025-01-09 11:58:53

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

如果你想要成员的地址,你应该写 &(test1.test2) 而不是 &test1+0x04 因为即使 IF 是一个 int 4 个字节,并且 IF 编译器没有填充结构,并且 IF 您或其他人没有更改类的内容,则 &test1+ 0x04 的真正含义“&test1 plus 4*sizeof(test) bytes”,这是达到 (&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");
        }
    }
};

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

Basically, you can't muck around with pointers like that. You generally can't rely on the compiler to coincidentally put meaningful data there.

If you want the address of members you should write &(test1.test2) not &test1+0x04 because even IF an int is 4 bytes and IF the compiler hasn't padded the structure and IF you or someone else hasn't changed the contents of the class, then &test1+0x04 really means "&test1 plus 4*sizeof(test) bytes", it's another way of reaching (&test1)[4] in terms of pointer-array-equivalence.

Also, you can't memcpy over classes in general and expect meaningful results, unless they are POD.

If you want to compare instances of a class, you should write a function which compares each of the members in turn.

You can't write a general-purpose method for this because C++ is not a reflective language. That means you can't write code which magically knows the names and types of the members of a class.

So, if you want to compare and patch data like this, you will need to do something like this:

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

You will have to write something like this for each class you want to patch.

不可一世的女人 2025-01-09 11:58:53

神奇的 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;
}

Where does the magic 0x04 and 4 come from?

If this works, it is just because of a particular alignment and implementation.
Better a more structured way:

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;
}
瑾夏年华 2025-01-09 11:58:53

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

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

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

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

&test1 + 0x04 means to add 4 times the sizeof testclass to the address of test1, and the resulting pointer has type testclass*. It would point to the fifth element of an array whose first element is at the address of test1, except that test1 isn't part of an array, so the addition has undefined behavior.

What you seem to want is to add 4 bytes to the address of test1. You can do that for example with ((char*)&test1) + 4, which results in a pointer of type char*. Beware that the standard doesn't guarantee that sizeof(int) is 4, nor does it guarantee that offsetof(testclass, test2) == sizeof(int).

You're permitted to inspect any object's memory as char* or unsigned char*. But there are some limits on how useful this ability is:

  1. Classes can have padding bytes in them, that can take any values. So just because two objects have different byte values in memory doesn't mean they aren't equal.
  2. Non-POD classes can have pretty much arbitrary "extra stuff" in them, put there by the implementation, and cannot safely be copied byte-wise.
  3. Classes with pointer members often cannot safely be copied byte-wise, and certainly not to another machine over the network.
  4. Even if the class is POD, if you send it over the network then you have to make sure that the class has the same layout on the two machines. This is not necessarily the case if the code was compiled with different options, by different compilers, or for different architectures, but sometimes it is the case.
茶花眉 2025-01-09 11:58:53

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 和其他标量完成)。

(我没有测试过代码)

1) At first, you need your class to be POD. At least you can take out data members to separate structure.

2) Then, if choose your offset granularity = 1, 2, 4 or 2^n bytes as necessary; and determine the type that suits this requirement: 2^n == sizeof(chunk_type). For example, you wish to byte-to-byte comparison, so cast you pointer to _State (or MyClass see #1) to desired type: (char*)this->m_state.

Here is the function, that tries to find the first chunk that differs in both classes, and returns its offset or -1 if no differences found.

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: Of course, your chunk_type must have == operator defined (this done already for char, int and other scalars).

(I've not tested the code)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文