加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 为什么不完整类型的智能指针数据成员和原始指针数据成员在

发布时间:2020-12-16 05:38:53 所属栏目:百科 来源:网络整理
导读:在以下代码中: 智能指针数据成员pImpl(类Impl)和原始指针pc(CAT类)都是不完整的数据类型,在Widget.h中没有这两个类的定义 //widget.h #ifndef W_H_#define W_H_#include memoryclass Widget { public: Widget(); ~Widget() { // delete pc; // I know I sho
在以下代码中:

智能指针数据成员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).

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读