使用memset将一个c++对象清零显然是灾难性的,特别是刚从c转过来的同学,就喜欢对定义的变量来个memset,比如std::string对象,结果可想而知。
c++里面专门有个概念叫做pod类型,所谓pod类型应该就是c里面的结构类型,对应的反义词就是c++的类类型
因此,在pod类型上调用memset清零是安全的,而非pod上则是禁止的。
pod类型的一个必要条件就是类型里没有用户自己定义的构造函数和析构函数,且其包含的成员变量也必须是pod类型
可以这样理解,既然没有用户定义的构造函数,其成员也没有,成员的成员也没有。。那这个类型的对象定义后,立即用memset来清零一定是安全的,
这是因为定义这个变量时只会为其申请内存,并没有执行任何构造函数之类的代码在它上面作初始化、分配资源等操作。
因此如果memset能判断当前初始化的内存地址是不是指向了pod类型变量,再作操作就会更安全了。
得益于c++的强类型系统,这个完全是可以在编译阶段实现的。
首先boost里面有个is_pod的模板,可以用来判断一个类型是不是pod类型
boost::is_pod<int>::value
boost::is_pod<std::string>::value
上面两个测试表达式的值一个是1,一个是0。即int被测试出是pod类型,而std::string不是
因此我们可以封装自己的memset函数,通过判断这个值来决定是不是需要执行memset操作:
更好的做法当然是能在编译期就找到问题,使用一个静态断言:
这样下面代码就不能编译通过了:
std::string s1;
pod_memset(&s1,0,sizeof(s1));
使用上面的类型来完成检测的memset函数:
在std::string对象上调用上述方法,编译器会报错:
1>------ Build started: Project: test, Configuration: Debug Win32 ------
1>Compiling...
1>test.cpp
1>e:\cj\boost_test\test\test.cpp(291) : error C2621: member 'is_pod_type<T>::CHECK::not_pod_type' of union 'is_pod_type<T>::CHECK' has copy constructor
1> with
1> [
1> T=std::string
1> ]
1> e:\cj\boost_test\test\test.cpp(293) : see reference to class template instantiation 'is_pod_type<T>::CHECK' being compiled
1> with
1> [
1> T=std::string
1> ]
1> e:\cj\boost_test\test\test.cpp(301) : see reference to class template instantiation 'is_pod_type<T>' being compiled
1> with
1> [
1> T=std::string
1> ]
1> e:\cj\boost_test\test\test.cpp(312) : see reference to function template instantiation 'void pod_memset<std::string>(T *,int,int)' being compiled
1> with
1> [
1> T=std::string
1> ]
1>Build log was saved at "file://e:\cj\boost_test\test\Debug\BuildLog.htm"
1>test - 1 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
c++里面专门有个概念叫做pod类型,所谓pod类型应该就是c里面的结构类型,对应的反义词就是c++的类类型
因此,在pod类型上调用memset清零是安全的,而非pod上则是禁止的。
pod类型的一个必要条件就是类型里没有用户自己定义的构造函数和析构函数,且其包含的成员变量也必须是pod类型
可以这样理解,既然没有用户定义的构造函数,其成员也没有,成员的成员也没有。。那这个类型的对象定义后,立即用memset来清零一定是安全的,
这是因为定义这个变量时只会为其申请内存,并没有执行任何构造函数之类的代码在它上面作初始化、分配资源等操作。
因此如果memset能判断当前初始化的内存地址是不是指向了pod类型变量,再作操作就会更安全了。
得益于c++的强类型系统,这个完全是可以在编译阶段实现的。
首先boost里面有个is_pod的模板,可以用来判断一个类型是不是pod类型
boost::is_pod<int>::value
boost::is_pod<std::string>::value
上面两个测试表达式的值一个是1,一个是0。即int被测试出是pod类型,而std::string不是
因此我们可以封装自己的memset函数,通过判断这个值来决定是不是需要执行memset操作:
template<class T> void pod_memset(T *p, int value, int len)
{
if (boost::is_pod<T>::value)
{
::memset(p, value, len);
}
}
更好的做法当然是能在编译期就找到问题,使用一个静态断言:
template<class T> void pod_memset(T *p, int value, int len)
{
boost::static_assert (boost::is_pod<T>::value);
::memset(p, value, len);
}
这样下面代码就不能编译通过了:
std::string s1;
pod_memset(&s1,0,sizeof(s1));
不使用boost库的工程如何实现呢,下面是采用c++的union实现的一个版本,c++约定,非pod类型不能作为union的成员,利用这点,我们写出了下面的检测类型
template<class T>
struct is_pod_type
{
union CHECK
{
T not_pod_type;
} ;
};
使用上面的类型来完成检测的memset函数:
template<class T> void pod_memset(T *p, int value, int len)
{
typedef is_pod_type<T>::CHECK C;
::memset(p, value, len);
}
在std::string对象上调用上述方法,编译器会报错:
1>------ Build started: Project: test, Configuration: Debug Win32 ------
1>Compiling...
1>test.cpp
1>e:\cj\boost_test\test\test.cpp(291) : error C2621: member 'is_pod_type<T>::CHECK::not_pod_type' of union 'is_pod_type<T>::CHECK' has copy constructor
1> with
1> [
1> T=std::string
1> ]
1> e:\cj\boost_test\test\test.cpp(293) : see reference to class template instantiation 'is_pod_type<T>::CHECK' being compiled
1> with
1> [
1> T=std::string
1> ]
1> e:\cj\boost_test\test\test.cpp(301) : see reference to class template instantiation 'is_pod_type<T>' being compiled
1> with
1> [
1> T=std::string
1> ]
1> e:\cj\boost_test\test\test.cpp(312) : see reference to function template instantiation 'void pod_memset<std::string>(T *,int,int)' being compiled
1> with
1> [
1> T=std::string
1> ]
1>Build log was saved at "file://e:\cj\boost_test\test\Debug\BuildLog.htm"
1>test - 1 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========