cam*_*ino 10 c++ smart-pointers c++11
在以下代码中:
智能指针数据成员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
Run Code Online (Sandbox Code Playgroud)
//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)
{}
Run Code Online (Sandbox Code Playgroud)
//main.cpp
#include "widget.h"
int main()
{
Widget w;
}
Run Code Online (Sandbox Code Playgroud)
//输出
IMPL
猫
〜默认地将Impl
对于原始指针数据成员,在销毁窗口小部件对象时不会调用其destuctor .
在shared_ptr数据成员中,其析构函数已被正确调用.
据我所知,在Widget :: ~Widget()中,它应该自动生成一些代码,如下所示:
~Widget() {
delete pc; // wrote by me
// generated by compiler
delete pImpl->get();
}
Run Code Online (Sandbox Code Playgroud)
为什么当窗口小部件被破坏时,shared_ptr数据成员和原始数据成员具有不同的行为?
我在Linux中使用g ++ 4.8.2测试代码
================================编辑================= ==============根据答案,原因是因为:
编译器生成的代码不是:
~Widget() {
delete pc; // wrote by me
// generated by compiler
delete pImpl->get();
}
Run Code Online (Sandbox Code Playgroud)
它可能是这样的:
~Widget() {
delete pc; // wrote by me
// generated by compiler
pimpl.deleter(); //deleter will be initailized while pimpl object is initialized
}
Run Code Online (Sandbox Code Playgroud)
dan*_*nca 13
因为您在头文件中转发声明"CAT",所以您的数据类型不完整.使用此信息,编译器会陷入未定义的行为,并且可能无法调用析构函数.
标准说的是什么
如果被删除的对象在删除时具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为是未定义的.
在这里你可以找到一个详细的解释:为什么,真的,删除一个不完整的类型是未定义的行为? 只需将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
};
Run Code Online (Sandbox Code Playgroud)
和输出
Impl
CAT
~CAT
~Impl
Run Code Online (Sandbox Code Playgroud)
前向声明可以加快编译时间,但是当需要有关数据类型的更多信息时可能会导致问题.
但为什么它适用于智能指针?这是一个更好的解释:删除指向不完整类型和智能指针的指针
基本上,shared_ptr在初始化或重置指针时只需要声明.这意味着它在声明的那一刻不需要完整的类型.
此功能不是免费的:shared_ptr必须创建并存储指向删除器仿函数的指针; 通常这是通过将删除器存储为存储强弱引用计数的块的一部分,或者通过将指针作为指向删除器的块的一部分来存储(因为您可以提供自己的删除器).
您正在尝试删除不完整类型的对象,如果要删除的对象类型具有非平凡的析构函数,则这是未定义的行为.
有关此事的更多信息,请参阅[expr.delete]标准以及以下链接:
注意:析构函数Widget::Cat是非平凡的,因为它是用户声明的; 反过来,这意味着它没有被调用.
要解决问题,只需提供定义,Widget::Cat以便在执行时不会完整delete pc.
它在使用shared_ptr时的工作原理是,在实际构造shared_ptr实例之前,"删除点"不会发生(通过;即,实际上实例化了Deleter).make_shared