C++常用方法和技巧--POD类型

(46) 2023-10-17 13:12

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说C++常用方法和技巧--POD类型,希望能够帮助你!!!。

正文之前,先请看一段代码,并思考:

1)对象a1和a2之间能否使用memcpy?

2)对象b1和b2之间能否使用memcpy?

struct A {};
struct B {};
struct A1 : A
{
    A x;
    int i;
};

struct A2 : A
{
    B x;
    int i;
};

struct B1 : A
{
	int i;
	A x;
};

struct B2 : A
{
	int i;
	B x;
};
void main()
{
	A1 a1;
	A2 a2;
	B1 b1;
	B2 b2;
	std::cout << "a1和a2:" << std::endl;
	std::cout <<"a1 :" << &a1 << std::endl; //
	std::cout << "a2.x :" << &(a1.x) << std::endl; //
	std::cout << "a2 :" << &a2 << std::endl; //
	std::cout << "a2.x :" << &(a2.x) << std::endl; //
  
	std::cout << "b1和b2:" << std::endl;
	std::cout << "b1 :" << &b1 << std::endl; //
	std::cout << "b1.i :" << &(b1.i) << std::endl; //
	std::cout << "b2 :" << &b2 << std::endl; //
	std::cout << "b2.i :" << &(b2.i) << std::endl; //
}

一 POD类型的定义

POD类型是英文中Plain Old Data的缩写。POD在C++中是一个非常重要的概念,通常用于说明一个类型的属性,尤其是用户自定义类型的属性。

POD意如其名。Plain,表示了POD类型是个普通的类型,在C++中常见的类型都有这样的属性,而不像一些存在着虚函数虚继承的类型那么特别。而Old类型则体现了其与C的兼容性,比如可以用最老的memcpy()函数进行复制,使用memset()进行初始化等。具体地, POD类型划分为两个基本概念的合集,即:平凡的(trivial)和标准布局的(standar layout)。

1 平凡的

1) 拥有平凡的默认构造函数(trivial constructor)和析构函数(trivial destructor)

2) 拥有平凡的拷贝构造函数(trivial copy constructor)和移动构造函数(trivial move constructor)

3) 拥有平凡的拷贝赋值运算符(trivial assignment operator)和移动赋值运算符(trivial move operator)

4) 不能包含虚函数和虚基类

判断类型是否是平凡的,可用标准库中的is_trivial方法,头文件<type_traits>

template <typename T> struct std::is_trivial;

2 标准布局

1) 所有非静态成员有相同的访问权限(public,private,protected)

2) 在类或结构体继承时,满足下列两种情况之一:

  • 派生类中有非静态成员,且只有一个仅包含静态成员的基类
  • 基类有非静态成员,而派生类中没有非静态成员

3) 类中第一个非静态成员的类型与其基类不同

4) 没有虚函数和虚基类

5) 所有非静态数据成员均符合标准布局类型,其基类也符合标准布局

判断类型是否是平凡的,可用标准库中的is_standard_layout方法,头文件<type_traits>

template <typename T> struct std::is_standard_layout;

二 POD类型的作用

我们先来运行一下文章开头的代码,看下执行结果:

C++常用方法和技巧--POD类型_https://bianchenghao6.com/blog__第1张

从结果中看出: b1.i和b1的地址相同,b2.i和b2的地圸相同;a1.x和a1的地址不同,a2.x和a2的地址相同。

用下图来示意下a1、a2、b1、b2的内存结构:

C++常用方法和技巧--POD类型_https://bianchenghao6.com/blog__第2张

C++常用方法和技巧--POD类型_https://bianchenghao6.com/blog__第3张

因此,b1和b2间可以用memcpy,a1和a2是不可以的。

特别说明:不同的编译器所得结果不同,以上为gcc测试效果,在msvc中测试,a1.x和a1的地址是相同的。

对于POD而言,就是平凡的和标准布局的两个方面,判别方法:

template <typename T> struct std::is_pod;
//is_pod::value<T>==true等同于,is_standard_layout::value<T>==true 并且is_trivial::value<T>==true.

查看A1、A2、B1、B2是否为POD类型:

std::cout << "A1:" << std::is_pod<A1>::value << std::endl;		// 输出 0
std::cout << "A2:" << std::is_pod<A2>::value << std::endl;		// 输出 1
std::cout << "B1:" << std::is_pod<B1>::value << std::endl;		// 输出 1
std::cout << "B2:" << std::is_pod<B2>::value << std::endl;		// 输出 1

因此,A1不是POD类型,至于A1不符合前面讲的哪一条定义,自己对着定义找去吧。

使用POD好处:

  • 字节赋值,可以安全地使用memset和memcpy对POD类型进行初始化和拷贝等操作;
  • 提供对C内存布局兼容。C++程序可以与C函数进行相互操作,因为POD类型的数据在C与C++间的操作总是安全的;

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

上一篇

已是最后文章

下一篇

已是最新文章

发表回复