C++:如何阻止函数接受内联分配的指针?

Cas*_*sey 5 c++ parameters pointers function allegro

无法弄清楚如何准确地说出问题,所以这是一个例子:

鉴于此功能原型:

void Foo(myClass* bar);
Run Code Online (Sandbox Code Playgroud)

我想阻止这种用法:

Foo(new myClass());
Run Code Online (Sandbox Code Playgroud)

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

myClass* bar = NULL;
bar = new myClass();
Foo(bar);
Run Code Online (Sandbox Code Playgroud)

要么

myClass bar;
Foo(&bar);
Run Code Online (Sandbox Code Playgroud)

谢谢.


编辑

这是一个澄清的例子:


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;
}
Run Code Online (Sandbox Code Playgroud)

无论用户传入的图像是什么,都会被复制到对象的图像中.

如果我在复制后取消分配传入的图像并且图像在程序的其他地方使用,则会使程序崩溃并导致访问冲突.

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

关于使用替代库或Allegro库本身的评论将被忽略,我已经知道它太可怕了.我没有选择.

GMa*_*ckG 19

您的设计需要做出选择.取得所有权并删除它,或者不取得所有权.无论哪种方式,都由用户知道如何使用您的功能.他们要么需要知道你的功能会破坏图像(也许可以根据需要传递自己的副本),要么他们需要足够聪明来管理自己的资源.

通常情况下,您不想仅仅为了删除它而窃取所有权.所以我不会删除任何东西.如果有人愚蠢到无法删除他们传递的图像,那就不是这个功能问题了.换句话说,你应该设法保护免受墨菲的影响,但忘记防范马基雅维利.

也就是说,原始指针的使用很糟糕!糟糕的C++代码标志着手动资源管理和资源问题.你应该在图像周围有一个包装器,它将删除析构函数中的图像.这样,即使抛出异常,也永远不会泄漏.为它提供一种reset()丢弃旧图像资源并获取新图像资源的方法.

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

这是在一个名为的智能指针中完成的shared_ptr.Boost有一个,TR1有一个,C++ 0x有一个.只需给它一个自定义删除(一个释放图像),你再也不用担心资源管理了.

这应该用所有资源完成.这里的概念是Scoped-bound资源管理(SBRM); 通过利用自动(堆栈)变量的生存期规则自动管理资源.它是已知的alos,因为它是原始但更丑陋的名称资源获取是初始化(RAII).对这个领域做一些研究,你会发现你的代码更简单,更清洁.


如果不改变参数的类型,就无法完成.您可以将其更改为:

void Foo(myClass*& bar);
Run Code Online (Sandbox Code Playgroud)

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

void foo(int*&);

int main(void)
{
    int *i = 0;
    int j;

    foo(i); // well-formed
    foo(&j); // ill-formed
    foo(new int); // ill-formed
}
Run Code Online (Sandbox Code Playgroud)

但是,这不允许获取左值的地址.你当然可以做到这一点:

int main(void)
{
    int j;
    int* pj = &j;
    foo(pj); // well-formed
}
Run Code Online (Sandbox Code Playgroud)

它有效.但我不知道你为什么要这样做.


上面的解决方案允许你修改参数(因为它是一个引用).如果你想在函数中强制执行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*>);
Run Code Online (Sandbox Code Playgroud)

相同的结果,除了你在函数中有一个const引用.


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

foo(new int);
Run Code Online (Sandbox Code Playgroud)

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