C++ POD与结构体声明

时间:2022-08-30 18:25:18

请看下面这个结构体定义:

struct MyStruct 
{ 
    int i; 
    wstring str; 
    vector<wstring> strs; 
};


上面的结构休定义有问题吗?理论上说没有,但在某些情况下就有问题了。如下用法:

MyStruct *pMy = new MyStruct(); 
memset(pMy, 0, sizeof(MyStruct)); 
pMy->i = 100; 
pMy->str = L"leehong"; 
// Here will crash, vector is not POD, so memset() function or ZeroMemory macro 
// do not know how to initialize the member, or it will lead some infos of pstr member 
// lost and crash when access. pMy->pstr.push_back(L"One");


如果这样用的话,在Debug下运行没有问题,但在Release下运行,就会在push_back处Crash。这是为什么呢?这就引出下面要讲的POD了。

 

什么是POD

 

POD(Plain Old Data)指的是能够像C语言中的结构体那样进行处理的一种数据类型,比如能够使用memcpy()来复制内存,使用memset()进行初始化等。

在C++ 98标准中,POD实际上是受限于结构体定义中的语言特性而定义的。

struct S { int a; }; //S就是一个POD 
struct SS { int a; SS(int aa) : a(aa) { } }; // SS就不是POD了 
struct SSS { virtual void f(); };


在C++0x中,POD被定义为可以简简单单复制的,类型普通的,并且拥有可以应对多种POD原先就能支持的操作的标准变量地址布局(?)。POD的定义和以前差不多:

1)如果你所有的成员变量和基类都是POD,那么这个类型就是POD。

2)POD应当满足以下要求,

     a)没有虚函数

     b)没有虚基类

     c)没有引用

     d)没有多重访问

新标准对POD最大的影响就是,对于拥有不会影响数据分配布局的构造函数的数据结构,也可以算作是POD。

关于POD详细信息,请参考官方文档:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2294.htmlC++ POD与结构体声明

 

Crash原因分析 

 

回到最上面举的那个例子,结构体初始化时,首先会调用它自动生成的默认构造函数,同时也会调用vector<wstring>的构造函数为pstr成员生成一块内存。然后我们都知道,使用结构体一般都会用memset 或 ZeroMemory函数来把结构体清成0,那么当我们调用ZeroMemory后,之前为pstr生成的内存就变成了0,这样当在后面再访问这个成员时,就会导致Crash。

所以一般情况下在结构体中不要定义一些复杂的数据类型,如果无法避免,就声明这种类型的指针,因为指针肯定是一个POD的数据。

 

解决方法

 

1)上面的那种方法可以把pstr换成指针类型,也就是vector<wstring>* 在调用ZeroMemory后,再对这个变量分配内存。

2)为这个结构体写构造函数,在这个构造函数里面进行初始化,在使用时永远不要调用ZeroMemory,这样的话,struct就实际上跟class的用法一样了。