• 为什么不能将ptr_fun(operator delete)传递给For_Each


    正在写的一段程序,需要将指针存入一个数组。出于简洁的考虑,不想用智能指针——若要使用智能指针,则必须再给智能指针加上一层包装,以屏蔽掉穿透性的取址操作符(见ATL::CAdapt)。

    因此,析构元素的操作就不得不自已动手了——erase前,得先对要删除的元素手动delete。

    std::for_each(objects.begin(), objects.end(), …);

    在清空数组时,需要对每个元素调用delete,很自然写出for_each时,问题来了,第三个参数传什么?stl提供了类似C#委托的函数包装对象,可以把全局delete关键词封装为函数对象吗?为此,特意百度了一下。结果还真有,像下面这样:

    std::for_each(objects.begin(), objects.end(), ptr_fun(operator delete));

    不过,讨论者也说了,这个使用可以编译通过,但是只能释放内存,不能调用析构函数。有人提出了一些理由,诸如对迭代器的解引用,以及operator delete与delete operator有差别等等,以及thinking in C++中的解释。大伙的讨论让我看得很晕,但真正的理由,在我看来只有一个,那就是类型信息的缺失。

    我们知道,delete执行两个操作:

    1. 调用对象的析构函数。

    2. 将对象内存归还堆。

    要执行第一步操作,就必须知道对象的类型。delete如何知道对象是什么类型?这只能通过调用处的代码上下文与多态支持,而不可能是运行时类型识别——最初的C++并不支持运行时类型识别,而且,就算支持,C++对效率的一贯追求也不允许在delete时使用运行时类型识别。

    因此,在调用delete时,必须传递目标类型或其基类的指针,当传递基类指针时,要求该类族必须使用虚拟析构函数。C++类的自定义delete操作符以void*作为参数,这似乎误导了一些人。很显然的是,delete也需要知道对象的具体类型,才能将调用转交给该类型的自定义delete。有了delete的保证,自定义delete操作符并不需要检查它的参数类型。而且作为一个类型成员,它也不会不知道所属的类型。因此,自定义delete以void*为参数并不代表delete不需要类型信息。或许C++要求语法如此只是为了保证函数签名的一致?

    假如我们要自己实现一个delete函数,这个函数只能是一个模板函数,就像std::_Destroy一样。

    template<typename T>

    void Delete(T* pObj) { pObj->~T(); free(pObj); }

    对于开头处提到的ptr_fun(operator delete),这里delete并没有调用上下文,于是它退化成了一个类似void delete(void*)的函数。假若delete是一个模板函数,那么我们可以这么写:

    ptr_fun(operator delete<CMyClass>)

    但是很遗憾,没有这样的语法支持。

    解决这个问题的唯一办法就是,写一个模板函数,接收模板类型的指针类型,然后在函数内部调用delete。这个模板函数可以传递给ptr_fun。有两点小要求:

    1. 这个模板函数不能返回void,它必须有返回值,原因请看ptr_fun生成对象的operator()——该操作符会用目标函数的返回值来调用return。而编译器不支持return void这样的语法。

    2. 模板函数必须在调用ptr_fun之前显式实例化,否则将无法链接成功。

    class TypeA
    {
    public:
        ~TypeA() 
        {
            cout<<"destruct"<<endl;
        }
    };
    
    template<typename T>
    int Delete(T* obj)
    {
        delete obj;
        return 1;
    }
    
    template int Delete(TypeA*);
    
    int mainPtrFunDelete()
    {
        TypeA* obj = new TypeA();
        //std::ptr_fun(::delete<TypeA>)(obj);
        std::ptr_fun(Delete<TypeA>)(obj); 
        return 1;
    }
  • 相关阅读:
    深圳成为全球第一个100%电动公共汽车的城市
    layui 数据表格按钮事件绑定和渲染
    Layui 改变数据表格样式覆盖
    js 遍历删除数组
    layui 数据表格最简单的点击事件
    layui 数据表格使用
    Layui 解决动态图标不动的问题
    Js 改变时间格式输出格式
    PHP 面向对象的数据库操作
    PHP SQL预处理
  • 原文地址:https://www.cnblogs.com/yedaoq/p/2149998.html
Copyright © 2020-2023  润新知