为什么有些类型无法使用memset初始化?

70 篇文章 0 订阅
37 篇文章 0 订阅

基本经验

我们知道,对于这个问题,一般的开发经验是一些基本数据类型的,像int, char, double之类的构造的简单struct类型,我们在C语言中,一般使用memset来对一片内存进行初始化。而另一个经验又是,有些类型,我们不能使用memset进行初始化,例如:string, vector之类的stl结构,或者是一些自定义的类型比较复杂的,例如有多态性质的类定义。

什么样的类可以使用memset初始化

我们这里需要引入一个概念来描述这个问题,叫POD对象。引用维基百科里面的描述如下:

A Plain Old Data Structure in C++ is an aggregate class that contains only PODS as members, has no user-defined destructor, no user-defined copy assignment operator, and no nonstatic members of pointer-to-member type.

简单来说,就是一个类中,只包含一些基本成员,没有构造,没有析构,没有拷贝构造。memset是C语言中的初始化函数,用于C语言中继承过来的结构体是安全的,但不能用于C++中定义的非POD结构体。

为什么non-POD结构体不能使用memset

看了国内的开发人员写的文章,感觉没有任何一个把这个问题讲明白,或者讲到点子上。以string为例,相信不少人有相关的经验,就是用了memset去初始化这个对象。大致的说法,是memset把类的内部数据搞乱了。之前我也是这么认为的,可是再仔细一推敲,内部数据怎么会搞乱?这不是扯蛋么,无非就是一些成员和指针,那我指针初始化为0怎么了,那为什么会崩溃呢?那一定是你对问题的理解没到点子上!

真正原因

那么好,写一段程序来试一下呗!

class Base {
public:
    Base() {
        printf("constructor Base!\n");
    }   
    virtual ~Base() {
        printf("destruction Base!\n");
    }   
    virtual void Print() {
        printf("Base printf\n");
    }   
    int i;
};

class Derived: public Base {
public:
    Derived() {
        printf("Contructor Derived!\n");
    }   
    virtual ~Derived() {
        printf("destruction Derived!\n");
    }   
    virtual void Print() override {
        printf("Derived printf\n");
    }   
};

int main(int argc, char** argv) {
    Base* base = new Derived();
    memset(base, 0, sizeof(Derived));
    printf("after memset!\n");
    delete base;
    printf("after delete base!\n");
    return 0;
}

执行结果如下:

[root@izwz93gpxv4kgv8rwosd06z test]# ./test       
constructor Base!
Contructor Derived!
after memset!
Segmentation fault

我们看到,与之前猜想到的一致,memset其实并不是直接让程序崩溃了,这个类在执行memset之后,并不会马上崩溃,无非就是这个类对象对应的内存区全部重置为0而已,为什么要崩溃?而什么时候崩溃,那是后续的操作导致程序崩溃了!后续的delete操作的时候,因为是一个虚函数调用,需要从虚函数表中找到对应的函数地址进行调用,那此刻,**对应的析构函数的地址是从虚函数表地址来进行查找,而此时,虚函数表地址已经被memset置为0了,调用了非法内存,崩溃出来了!这才是真正的根本原因!**关于虚函数表的理解,可以参考陈皓老师的文章,里面讲的非常清晰,文章链接C++ 虚函数表解析

总结

1、memset只适用于POD类型结构。对非POD类型,用构造函数来初始化。
2、memset让非POD崩溃的根本原因,是把里面的数据(例如虚函数表),即指向虚函数表的指针置空之后,导致后续的一些函数调用会访问到非法内存,这才是崩溃的根本原因。
3、认识问题,应该有寻根问底的精神,否则,你的认识就只是停留在表面,而无法认识到问题的本质,水平也就一直停留在低水平层次。

  • 8
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值