智能指针数据成员pImpl(类Impl)和原始指针pc(CAT类)都是不完整的数据类型,在Widget.h中没有这两个类的定义
//widget.h
#ifndef W_H_ #define W_H_ #include <memory> class Widget { public: Widget(); ~Widget() { // delete pc; // I know I should put ~Widget to .cpp // I just want to show the difference in behavior // between raw pointer and smart pointer(both has incomplete type) // when widget object destructs } private: struct Impl; std::shared_ptr<Impl> pImpl; // use smart pointer struct CAT; CAT *pc; //raw pointer }; #endif
//widget.cpp
#include "widget.h" #include <string> #include <vector> #include <iostream> using namespace std; struct Widget::CAT { std::string name; CAT(){cout<<"CAT"<<endl;} ~CAT(){cout<<"~CAT"<<endl;} }; struct Widget::Impl { std::string name; Impl(){cout<<"Impl"<<endl;} ~Impl(){cout<<"~Impl"<<endl;} }; Widget::Widget() : pImpl(std::make_shared<Impl>()),pc(new CAT) {}
//main.cpp
#include "widget.h" int main() { Widget w; }
//输出
Impl
CAT
~Impl
对于原始指针数据成员,当小部件对象被破坏时,它的destuctor不会被调用.
据我了解,在Widget ::〜Widget()中应该生成一些
代码自动如下:
~Widget() { delete pc; // wrote by me // generated by compiler delete pImpl->get(); }
为什么在widget被破坏的时候,shared_ptr数据成员和原始数据成员有不同的行为?
我在Linux中使用g 4.8.2测试代码
================================编辑================= ==============
根据答案,原因是因为:
~Widget() { delete pc; // wrote by me // generated by compiler delete pImpl->get(); }
它可能是这样的:
~Widget() { delete pc; // wrote by me // generated by compiler pimpl.deleter(); //deleter will be initailized while pimpl object is initialized }
解决方法
什么标准说
if the object being deleted has incomplete class type at the point of
deletion and the complete class has a non-trivial destructor or a
deallocation function,the behavior is undefined.
在这里可以找到详细的说明:Why,really,deleting an incomplete type is undefined behaviour?
只需将struct声明移动到类定义之前才能解决您的问题:
struct CAT { std::string name; CAT(){std::cout<<"CAT"<<std::endl;} ~CAT(){std::cout<<"~CAT"<<std::endl;} }; class Widget { public: Widget(); ~Widget() { delete pc; // I know we should put this code to cpp // I am just want to show the difference behavior // between raw pointer and smart pointer // when widget object destruct } private: struct Impl; std::shared_ptr<Impl> pImpl; // use smart pointer CAT *pc; //raw pointer };
并输出
Impl CAT ~CAT ~Impl
转发声明有助于加快编译时间,但是当需要有关数据类型的更多信息时可能会导致问题.
但为什么它可以用于智能指针?
这是一个更好的解释:Deletion of pointer to incomplete type and smart pointers
基本上,在初始化或重置指针时,shared_ptr只需要声明.这意味着在声明的时刻不需要完整的类型.
This functionality isn’t free: shared_ptr has to create and store a pointer to the deleter functor; typically this is done by storing the deleter as part of the block that stores the strong and weak reference counts or by having a pointer as part of that block that points to the deleter (since you can provide your own deleter).