c – 为什么不完整类型的智能指针数据成员和原始指针数据成员在其父级破坏时具有不同的行为?

前端之家收集整理的这篇文章主要介绍了c – 为什么不完整类型的智能指针数据成员和原始指针数据成员在其父级破坏时具有不同的行为?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
在以下代码中:

智能指针数据成员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不会被调用.

而shared_ptr数据成员,其析构函数已被正确调用.

据我了解,在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
        }

解决方法

因为您在头文件中转发声明“CAT”,所以您的数据类型不完整.使用这些信息,编译器将进入未定义的行为,析构函数可能不被调用.

什么标准说

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).

猜你在找的C&C++相关文章