代码之家  ›  专栏  ›  技术社区  ›  zwol

自动转换为裸指针但不能显式删除的C++“智能指针”模板

  •  5
  • zwol  · 技术社区  · 14 年前

    我在一个非常大的传统C++代码库中工作,它将一直是无名的。作为一个遗留的代码库,它到处传递原始指针。但我们正在逐步尝试使其现代化,因此也有一些智能指针模板。这些智能指针(不同于Boost的scoped\u ptr)有一个到原始指针的隐式转换,这样您就可以将其中一个指针传递到一个例程中,该例程接受原始指针,而不必进行写操作 .get() . 这样做的一个很大的缺点是,你也可能会意外地在一个应用程序中使用一个 delete 声明,然后你有一个双重免费的错误,这可能是一个真正的痛苦追踪。

    有没有办法修改模板,使其仍然隐式转换为原始指针,但如果在delete语句中使用,会导致编译错误?这样地:

    #include <my_scoped_ptr>
    
    struct A {};
    extern void f(A*);
    
    struct B
    {
        scoped_ptr<A> a;
    
        B();
        ~B();
    };
    
    B::B()
        : a(new A)
    {
        f(a); // this should compile
    }
    
    B::~B()
    {
        delete a; // this should NOT compile
    }
    
    5 回复  |  直到 14 年前
        1
  •  7
  •   Johannes Schaub - litb    14 年前

    标准上说

    您可以(ab)-通过声明转换函数的const版本来使用没有重载解析的情况。在一个一致的编译器上,这足以使它不再工作 delete :

    struct A {
      operator int*() { return 0; }
      operator int*() const { return 0; }
    };
    
    int main() {
      A a;
      int *p = a; // works
      delete a; // doesn't work
    }
    

    结果如下

    [js@HOST2 cpp]$ clang++ main1.cpp
    main1.cpp:9:3: error: ambiguous conversion of delete expression of type 'A' to a pointer
      delete a; // doesn't work
      ^      ~
    main1.cpp:2:3: note: candidate function            
      operator int*() { return 0; }
      ^
    main1.cpp:3:3: note: candidate function             
      operator int*() const { return 0; }
      ^
    1 error generated.
    

    删除 不期望 特别

    template<typename T>
    operator T*() { return /* ... */ }
    

    但是,这有一个缺点,即您的smartpointer现在可以转换为 任何 指针类型。虽然实际的转换仍然是typechecked,但这并不排除前面的转换,而是在以后给出一个编译时错误。遗憾的是,SfimaE似乎不可能用C++ 03中的转换函数:一种不同的方式是从其他函数返回私有嵌套类型指针。

    struct A {
      operator int*() { return 0; }
    
    private:
      struct nested { };
      operator nested*() { return 0; }
    };
    

    现在唯一的问题是转换成 void* ,在这种情况下,两个转换函数都是同样可行的。@Luther建议的一个解决方法是从另一个转换函数返回一个函数指针类型,该函数可以与GCC和Comeau一起工作,并消除 与模板解决方案不同,在通常的转换路径上没有其他问题

    struct A {
      operator int*() { return 0; }
    
    private:
      typedef void fty();
      operator fty*() { return 0; }
    };
    

    不过,请注意,这些变通方法仅适用于不一致的编译器。

        2
  •  4
  •   GManNickG    7 年前

    没有办法阻止一个也不能阻止另一个。任何可以隐式转换为函数调用指针的地方,都可以隐式转换为delete表达式。


    I'm wrong. :(

        3
  •  1
  •   In silico    14 年前

    您可以使用 Boost delete 在由 -> 接线员,所以你真的无法阻止一个坚定的白痴在你想出的任何机制周围工作。

    你真的应该实现一个 get() 方法而不是提供 operator T*() 所以至少打电话给 delete smartptr 不会编译。非白痴应该能够明白,这可能是不起作用的原因。

    是的,打字要花更多的时间 LegacyFunc(smartptr.get()) LegacyFunc(smartptr) ,但前者是首选的,因为它使其显式,并防止发生意外的转换,如 删除smartptr .

    如果你有这样的功能:

     void LegacyOwnPointer(SomeType* ptr);
    

    函数将在何处存储指针?这将破坏智能指针,因为现在它不知道其他东西拥有原始指针。

    不管怎样,你都有工作要做。智能指针类似于原始指针,但它们并不相同,因此您不能仅查找和替换的所有实例 T* my_scoped_ptr<T> 希望它能像以前一样工作。

        4
  •  0
  •   Steve Townsend    14 年前

    我没想太多但是。。。你能为操作符delete提供一个重载吗?这个重载对于模板类的实例是强类型的,这样当包含代码时编译就失败了?如果这是在您的头文件,那么隐式转换调用删除应防止赞成调用您的重载。

    {

        5
  •  0
  •   jyoung    14 年前

    我知道你不想在哪里大量应用.get()。你有没有考虑过用更小的替代delete?

    struct A
    {
        friend static void Delete( A* p) { delete p; }
    
    private:
        ~A(){}
    };
    
    struct B
    {
    };
    
    int main() 
       { 
    
        delete new B();  //ok
    
        Delete( new A ); //ok
    
        delete new A; //compiler error
    
        return (0); 
        }