C++中慎用逐位拷贝(bitwise copy)的函数

来源:互联网 发布:老男孩大数据百度云 编辑:程序博客网 时间:2024/05/20 11:21

1.简单介绍C语言中的memcpy(),memset(),memcmp()函数
  memcpy(),memset(),memcmp()等这些内存操作函数经常会帮我们完成一些数据复制、赋值等操作。因为C语言中,无论是内置类型,还是自定义类的结构类型(struct),其内存模型对于我们来说都是可知的,透明的。所以我们可以对该对象的底层字节序列一一进行操作,简单而有效。例如如下代码:

struct STUDENT{    char _name[32];    int _age;    bool _gender;}STUDENT a = {"Li Lei",20,true};STUDENT b = {"Han Meimei",19,false};int len = sizeof(STUDENT);STUDENT c;memset(&c,0,len);memcpy(&c,&a,len);char *data = (char*)malloc(sizeof(char)*len);memcpy(data,&b,len);

2.POD对象
  在C++中,我们把传统C风格的数据类型叫做POD(Plain Old Data)对象,即一种古老的纯数据,C的所有对象都是POD。一般来说,POD对象应该满足如下特性:其二进制内容是可以随意复制的,无论在什么地方,只要其二进制内容存在,我们就能准确无误地还原POD对象。正是由于这个原因,对于任何POD对象,我们都可以放心大胆地使用memset(),memcpy(),memcmp()等函数对对象的内存数据进行操作。
  C++的对象可能并不是一个POD,所以我们无法像在C中那样获得该对象直观简洁的内存模型。对于POD对象,我们可以通过对象的基地址和数据成员的偏移地址获得数据成员的地址。但是C++标准并未对非POD对象的内存布局做任何定义,对于不同的编译器,其对象布局是不同的。而在C语言中,对象布局仅仅会受到底层硬件系统差异的影响。
  针对非POD对象,其序列化会遇到一定的障碍:由于对象的不同部分可能存在于不同的地方,因为无法直接复制,只能通过手工加工序列化操作代码来处理对象数据。但是针对POD对象,这一切将不再困难:从基地址开始,直接按对象的大小复制数据,或者传输,或者存储。
  为什么C++中的对象有可能不是一个POD呢?这是由于C++的重要特性之一——多态,多态的一个基本支撑就是虚函数。在使用虚函数时,类的每一次继承都会产生一个虚函数表(virtual table),其中存放的是指向虚函数的指针, 这时候编译器会导入一个指向虚函数表的指针(vptr)到对象中。如果我们定义两个类:ZooAnimal和Bear:

class ZooAnimal {public:    ZooAnimal();    virtual ~ZooAnimal();    virtual void animate();    virtual void draw();    //...private:    //ZooAnimal的animate()和draw()    //所需要的数据}class Bear : public ZooAnimal {    Bear();    void animate();    void draw();    virtual void dance();    //...private:    //Bear的animate()、draw()和dance()    //所需要的数据};

  ZooAnimal对象以另一个ZooAnimal对象作为初值,或Bear对象以另一个Bear对象作为初值,都可以像POD对象那样逐位拷贝(bitwise copy)。例如以下代码:

Bear yoqi;Bear winnie = yoqi;

  yoqi的vptr被设定指向Bear类的virtual table。因此,把yoqi的vptr值拷贝给winnie的vptr是安全的。

  当一个基类对象以其派生类的对象内容作为初始化操作时,其vptr复制也必须保证安全,例如:

ZooAnimal franny = yoqi;

  franny的vptr不可以被设定指向Bear类的虚函数表,此时如果直接逐位复制的话就会导致这个结果,那么当下面程序片段中的draw()被调用而franny被穿进去时,就会炸毁(blow up)。

void draw(const ZooAnimal& zoey) {    zoey.draw();}void foo(){    ZooAnimal franny = yoqi;    draw(yoqi);    draw(franny);}

3.参考资料
《编写高质量代码——改善C++程序的150个建议》
《深度探索C++对象模型》

0 0
原创粉丝点击