深入C++ 之逐位拷贝(bitwise copy)
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
最近有重新翻阅的侯捷老师的《深度探索C++对象模型》一书,依然别有新意源源涌来,不禁感叹经典书籍的魅力,好了,话不多说,进入今天的正题--逐位拷贝(bitwise copy)。
提示:以下是本篇文章正文内容,下面案例可供参考
一、逐位拷贝(bitwise copy)是什么?
众所周知的c++是兼容c语言的,而在传统的c语言中,顺序控制的语言执行结果,打造了内存位置与内容的完美对应,数据之间的赋值,可以通过内存的拷贝来完成。因为C语言中的类型,除了内置的类型之外,自定义的结构体类型struct的内存模型对我们来说,也是可知的。所以逐位拷贝存在于c语言中是美好的。但是c++语言本身是面向对象的语言,本身具有的多态性,对于程序员本身来讲,是不完全可知的,加上编译器对c++的优化,造成了不是所有的c++对象都适合逐位拷贝来实现c++对象之间的赋值拷贝操作。
二、POD(Plain Old Data)对象
1.什么是POD
简单的讲,我们把c++中带传统C风格的数据类型叫做POD(Plain Old Data)对象,C的所有对象都是POD。POD对象的二进制是可以完美的赋值拷贝的,并且二进制数据可以还原出原POD对象。因此,在c++中可以利用像memset(),memcpy()这样的函数操作POD对象。
那么什么样的类型是POD类型的呢?class、struct、union,且不具有用户定义的构造函数、析构函数、拷贝算子、赋值算子;不具有继承关系,因此没有基类;不具有虚函数,所以就没有虚表;非静态数据成员没有私有或保护属性的、没有引用类型的、没有非POD类类型的(即嵌套类都必须是POD)、没有指针到成员类型的(因为这个类型内含了this指针)。
2.c++中的POD
而在c++11中POD被释义为两个推广情况:平凡(trivial)和标准布局(standard-layout)
1.平凡(trivial):
类型是平凡的(trivial),则可以静态初始化、可以用memcpy直接复制数据而不是必须用copy构造函数。其生存期始于它的对象的存储被定义,无须等到构造函数完成。平凡class或结构必须满足:
- 有平凡的缺省构造函数,可用这样的默认语法:(SomeConstructor() = default;)
- 有平凡的copy与move构造函数,可用默认语法.
- 有平凡的copy与move运算符,可用默认语法.
- 有平凡的destructor,不能是虚函数.
2. 标准布局(standard-layout)
意味着它是有序的并且安排其成员兼容于C语言。这要求满足:
- 没有虚函数
- 没有虚基类
- 所有非静态数据成员有相同的访问控制(public, private, protected)
- 所有非静态数据成员,包括在任何基类中的,存在于类继承体系中的一个类中
- 上述规则适用于所有基类与类继承体系中的所有非静态数据成员
- 没有同一类型的基类型被定义为第一个非静态数据成员
一个class/struct/union是POD,当它是平凡的、标准布局的,所有数据成员是POD.对象可以不满足其中一个但是满足另外一个。例如,类有复杂的move与copy构造函数,因此不是平凡的,但可能是标准布局因此能与C程序互操作。类似地,一个类的有public与private的非静态数据成员,因此不是标准布局,但可以是平凡的因此可以memcpy操作。
三、为什么C++中的对象有可能不是一个POD呢?
c++中的特点之一就是--多态,多态的核心就是虚函数,有虚函数自然就有虚函数表(vtbl)和指向虚函数表的指针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()
...
//所需要的数据
};
如果上述两个类的同类型对象之间赋值作为初值,则可以是安全的,可以实现逐位拷贝,如Bear mm; Bear nn = mm;
但是若 ZooAnimal ll = mm;
这样的话,内部mm的vptr指向的是Bear的虚表,而ll中的vptr指向的应该是ZooAnimal中的虚表,如果此时可以实现逐位拷贝的话,指针之间的覆盖,直接将导致多态出现问题,所以逐位拷贝在c++中,并不是所有的对象都适用的,视情况稳定。
该处使用的url网络请求的数据。
总结
可知,逐位拷贝在C语言中是美好的,但是c++中不是所有的对象都适用,要根据对象的具体情况而定,充分考虑vptr的初值设定等问题,更要判断对象是否为POD对象,以及编译器的扩张优化等。
参考:《深入探索c++对象模型》--侯捷