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

C++:如何防止函数接受在线分配的指针?

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

    不知道如何准确地说出这个问题,下面是一个例子:

    给定此函数原型:

    void Foo(myClass* bar);
    

    我想阻止这种用法:

    Foo(new myClass());
    

    而是需要以前创建的对象:

    myClass* bar = NULL;
    bar = new myClass();
    Foo(bar);
    

    myClass bar;
    Foo(&bar);
    

    谢谢。


    编辑

    下面是一个明确的例子:

    
    void Mouse::SetImage(BITMAP* image, int focusX, int focusY) {
        if(_image) {
            set_mouse_sprite(NULL);
            set_mouse_sprite_focus(0, 0);
            show_mouse(NULL);
            destroy_bitmap(_image);
            _image = NULL;
        }
        if(image) {
            _image = create_bitmap(image->w, image->h);
            clear_bitmap(_image);
            blit(image, _image, 0, 0, 0, 0, image->w, image->h);
        }
        if(image == NULL) {
            focusX = 0;
            focusY = 0;
        }
        _focusX = focusX;
        _focusY = focusY;
        _dirtyImage = true;
    }
    

    用户传递的任何图像都会被复制到对象的图像中。

    如果我在复制后取消分配传入的映像,并且该映像在程序中的其他位置使用,它将使程序崩溃并导致访问冲突。

    如果他们在线分配存储,而我不解除分配,就会发生内存泄漏。如果在运行程序的过程中多次调用setimage方法,则问题会更加复杂。

    关于使用备用库或Allegro库本身的评论将被忽略,我已经知道这很可怕。我别无选择。

    5 回复  |  直到 14 年前
        1
  •  19
  •   Community Egal    7 年前

    你的设计需要做出选择。要么取得所有权并将其删除,要么不取得所有权。 不管怎样,用户都要知道如何使用您的函数。 他们要么需要知道你的功能会破坏图像(并根据需要传递他们自己的副本),要么他们需要足够聪明来管理他们自己的资源。

    通常,您不想为了删除它而窃取所有权。所以我愿意 删除任何内容。如果有人愚蠢到失去删除他们传递的图像的能力,这不是功能问题。换句话说,你应该试着去保护墨菲,但是忘记保护马基雅维利。

    也就是说,原始指针使用不好!贫C++代码的特点是手工资源管理和资源问题。应该在图像周围有一个包装器,它将删除析构函数中的图像。这样你就可以了 从未 泄漏,即使抛出异常。提供一个 reset() 方法丢弃旧的图像资源并获取新的图像资源。

    听起来您想要共享所有权,所以您需要一个引用计数的资源包装器。然后问题就解决了:如果有人进行了“内联”分配,它将被放入共享指针中,然后在完成分配后自动删除。(更好的是 explicit 构造器,所以必须有人知道他们将共享资源。)

    这是在一个名为 shared_ptr . Boost 有一个,Tr1有一个,C++0x有一个。只需给它一个自定义删除(一个释放图像),你就不再担心资源管理了。

    这应该用 全部的 资源。这里的概念是 Scoped-bound Resource Management (SBRM);利用自动(堆栈)变量的生存期规则自动管理资源。它被称为alos,因为它是原始的,但更丑的名字。 Resource-Acquisition Is Initialization (RAII)。对这个领域做一些研究,你会发现你的代码更容易和更清晰。


    如果不更改参数的类型,则无法执行此操作。您可以将其更改为:

    void Foo(myClass*& bar);
    

    因为非常量引用只能绑定到左值:

    void foo(int*&);
    
    int main(void)
    {
        int *i = 0;
        int j;
    
        foo(i); // well-formed
        foo(&j); // ill-formed
        foo(new int); // ill-formed
    }
    

    但是,这不允许使用左值的地址。当然,您可以执行以下简单操作:

    int main(void)
    {
        int j;
        int* pj = &j;
        foo(pj); // well-formed
    }
    

    它起作用了。但我不知道你为什么要这么做。


    上面的解决方案将允许您修改参数(因为它是一个引用)。如果要在函数中强制执行const,可以使用如下实用程序:

    template <typename T>
    class require_lvalue
    {
    public:
        require_lvalue(T& pX) :
        mX(pX)
        {}
    
        const T& get(void) const
        {
            return mX;
        }
    
        operator const T&(void) const
        {
            return get();
        }
    
    private:
        // non-copy-assignable
        require_lvalue& operator=(const require_lvalue&);
    
        const T& mX;
    };
    
    void foo(require_lvalue<int*>);
    

    结果相同,只是函数中有一个常量引用。


    请注意,MSVC有一个bug,并接受此错误:

    foo(new int);
    

    在这两种情况下,即使它不应该(它不接受 new int() 然而,

        2
  •  4
  •   pagra    14 年前

    不可能有这样的用法区别。在所有情况下,它都是一个有效的参数。我真的不明白你为什么需要这个…

        3
  •  2
  •   Sadeq    14 年前

    所以不要使用指针…使用(左值)引用:

    void Foo(myClass& bar);
    
        4
  •  2
  •   Roman Nikitchenko    14 年前

    这不是解决你的任务吗? 但我还是推荐类似std::auto-ptr这样的例子。

    #include <iostream>
    
    void test(int *& ptr)
    {
        std::cout << *ptr << std::endl;
    }
    
    int main()
    {
    /* NEXT LINE WILL FAIL */
    //  test(new int(5));
    
        int *b = new int(5);
        test(b);
        delete b;
    
        return 0;
    }
    
        5
  •  -1
  •   C.J.    14 年前

    C或C++不会给你确定内存分配到什么地方,你的函数的参数。如果你想要更好的安全性,那就在.NET中编程。

    如果你想让它更安全,就不要完全改变你的函数签名来接受一个自动指针。这样,语义就变得清晰了,不应该混淆谁或者什么拥有内存。