〇、开场
1.今天编译新项目的工程,遇到一个Warning C4150的警告,观察之,查之,原来如此。
2.夜深的时候,一觉醒来,就不想再睡了,人的心事,总是越想越复杂,越想越烦躁,不如简简单单的看几行代码。
3.废话少说,开始吧。
一、体现
0.代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class A; class B { public: //构造函数在其它文件中 B(void); ~B(void) { //如果A中还有堆开辟,那么会有内存泄漏 delete m_pA; } A* m_pA; }; |
warning C4150: 删除指向不完整“Base”类型的指针;没有调用析构函数
2.解释
(1)Foreward declaration
使用前置声明(Foreward declaration)来声明class A,此时class A并没有完整的定义,表现为其后面的类只能申明其指针,或者引用,前置声明以后的类无法看到其类实体。一般使用一些智能指针的时候也会出现这个问题。
(2)Incomplete Type
由于A是Incomplete Type(不完整类型),编译器只是把A加入到符号表并标记A为一个class,编译器并不知道更多的信息,比如A的大小,有何成员函数等等。编译器也无法知道A所对应的析构函数(destructor),因此不会调用A的析构函数。
(3)delete
在调用delete来删除pA的时候,编译器可以知道pA所指的内存大小,因为内存分配器在内存块中保存了大小信息,可以正确释放A所占据的内存,此时的delete相当于free()。注意,但是如果是A在堆上开辟的内存(比如在构造函数中),那么这些内存将被保留。也就是说,如果A没有再在堆上开辟内存,那么也不会有内存泄漏。
三、后果
1.可能内存泄露
2.伴随的资源没有释放
四、解决方案
1.移进来A的头文件,去除A的Foreward declaration
2.如果考虑1可能带来的依赖,可将(delete pA)放到cpp中实现,在cpp中引入头文件,如果担心效率,可再放到nil的内联文件中
3.网上有人说:如果在其它地方调用了析构函数,那么可以用
1 |
delete (void *)pA; |
猜想,这个可能只有Placement new的时候才有可能用到这种情况。
五、总结
视警告如错误,勿以恶小而为之 勿以善小而不为。