复制课程的一部分

时间:2012-02-03 10:23:24

标签: c++ class heap memcpy

我想将类的一部分复制到缓冲区。如果类中有更改,我需要这样做才能查看,并通过网络发送它以更新服务器上的更改。

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

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

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

谢谢!

4 个答案:

答案 0 :(得分:2)

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

如果你想要成员的地址,你应该写&(test1.test2)而不是&test1+0x04因为即使 IF 一个int是4个字节而 IF 编译器没有填充结构, IF 你或其他人没有更改类的内容,那么&test1+0x04实际上意味着“&test14*sizeof(test)字节“,这是在指针数组等价方面达到(&test1)[4]的另一种方式。

另外,除非是POD,否则你不能memcpy一般地超过课程并期望有意义的结果。

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

您不能为此编写通用方法,因为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");
        }
    }
};

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

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

您可以将任何对象的内存检查为char*unsigned char*。但是这种能力有多大的限制:

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

答案 3 :(得分:0)

1)首先,您需要将您的班级设为POD。至少你可以把数据成员分成单独的结构。

2)然后,如果需要选择偏移粒度= 1,2,4或2 ^ n个字节;并确定适合此要求的类型:2^n == sizeof(chunk_type)。例如,您希望进行逐字节比较,因此将指针_State(或MyClass见#1)指向所需类型:(char *)this-&gt; 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和其他标量完成了。)

(我没有测试过代码)