C++:如何防止函数接受内联分配的指针?
无法弄清楚如何准确地表达问题,所以这里有一个例子:
给定这个函数原型:
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 库本身的评论将被忽略,我已经知道这很糟糕。我别无选择。
Couldn't figure out how to word the question accurately, so here's an example:
Given this function prototype:
void Foo(myClass* bar);
I want to prevent this usage:
Foo(new myClass());
and instead require a previously created object:
myClass* bar = NULL;
bar = new myClass();
Foo(bar);
or
myClass bar;
Foo(&bar);
Thanks.
EDIT
Here's a clarified example:
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;
}
Whatever image the user passes in gets copied to the object's image.
If I deallocate the passed in image after copying it and the image is used elsewhere in the program it will crash the program with an access violation.
If they allocate the storage in-line and I don't deallocated it, a memory leak occurs. The problem is compounded if they call the SetImage method multiple times over the course of the running program.
Comments about using alternative libraries or on the Allegro Library itself will be ignored, I already know it's horrible. I don't have a choice.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您的设计需要做出选择。要么取得所有权并删除它,要么不取得所有权。 无论哪种方式,都由用户决定如何使用您的函数。他们要么需要知道您的函数将破坏图像(并且可能根据需要传递他们自己的副本),要么他们需要足够聪明来管理自己的资源。
通常,您不想仅仅为了删除它而窃取所有权。所以我不会删除任何内容。如果有人愚蠢到失去了删除他们传递的图像的能力,那不是这个函数的问题。换句话说,你应该尝试防范墨菲,但忘记防范马基雅维利。
也就是说,使用原始指针是不好的!糟糕的 C++ 代码的特点是手动资源管理和资源问题。您应该在图像周围有一个包装器,它将在析构函数中删除该图像。这样,即使抛出异常,您也永远不会发生泄漏。为其提供一个
reset()
方法,该方法会丢弃旧的图像资源并获取新的图像资源。听起来您想要共享所有权,因此您需要一个引用计数的资源包装器。然后问题就解决了:如果有人进行“内联”分配,它将被放入共享指针中,然后在完成后自动删除。 (更好的是有一个
显式
构造函数,这样有人就必须知道他们将共享资源。)这是在一个名为
shared_ptr
的智能指针中完成的。 Boost 有一个,TR1 有一个,C++0x 有一个。只需给它一个自定义删除(释放图像的一个),您就再也不用担心资源管理了。这应该使用所有资源来完成。这里的概念是范围限定的资源管理< /a>(SBRM);通过利用自动(堆栈)变量的生命周期规则来自动管理资源。它也因其原始但丑陋的名称而闻名资源获取是初始化 (RAII)。对这个领域进行一些研究,您会发现您的代码更简单、更清晰。
如果不更改参数的类型,则无法完成此操作。您可以将其更改为:
因为非常量引用只能绑定到左值:
但是,这不允许获取左值的地址。您当然可以做简单的事情:
而且它有效。但我不知道你为什么想做这些。
上述解决方案允许您修改参数(因为它是一个引用)。如果您想在函数内强制执行 const,您可以创建一个如下所示的实用程序:
相同的结果,除非您在函数内有一个 const 引用。
请注意,MSVC 有一个错误,并接受这一点:
在这两种情况下,即使它不应该接受。 (但是,它不接受 new int() 。)
Your design needs to make a choice. Either take ownership and delete it, or don't take ownership. Either way, it's up to the user to know how to use your function. They either need to know that your function will destroy the image (and maybe pass their own copy as needed), or they need to be smart enough to manage their own resources.
Typically, you don't want to steal ownership away just to delete it. So I would not delete anything. If someone is silly enough to lose the ability to delete the image they pass, that's not this functions problem. In other words, you should try to protect against Murphy, but forget about protecting against Machiavelli.
That said, raw pointer use is bad! Poor C++ code is marked by manual resource management and resource issues. You should have a wrapper around an image, that will delete the image in the destructor. That way you can never leak, even if an exception is thrown. Provide it with a
reset()
method which discards it's old image resource and gets a new one.It sounds like you want shared ownership, so you'll want a reference counted resource wrapper. The issue is then solved: if someone does an "inline" allocation, it'll be put into the shared pointer and then deleted automatically when it's done. (And even better is to have an
explicit
constructor so someone has to know they'll be sharing the resource.)This is done in a smart pointer called
shared_ptr
. Boost has one, TR1 has one, and C++0x has one. Just give it a custom deleted (one that frees the image), and you never worry about resource management again.This should be done with all resources. The concept here is Scoped-bound Resource Management (SBRM); that a resource is managed automatically by taking advantage of the lifetime rules of automatic (stack) variables. It's known alos as it's original but uglier name Resource-Acquisition Is Initialization (RAII). Do some research into this area and you'll find your code is easier and cleaner.
It cannot be done without changing the type of the parameter. You could change it to:
Because a non-const reference can only be bound to an lvalue:
However, this disallows taking the address of an lvalue. You can of course do the simple:
And it works. But I don't know why you'd want to do any of this.
The above solution would allow you to modify the argument (because it's a reference). If you wanted to enforce const within the function, you could make a utility like this:
Same result, except you have a const-reference within the function.
Note that MSVC has a bug, and accepts this:
in both cases, even though it shouldn't. (It does not accept
new int()
, however.)不可能有这样的用法区别。在所有情况下它都是一个有效的参数。我真的不明白你为什么需要这个......
It's impossible to have such a distinction of usage. And in all the cases it's a valid parameter. I really can't understand why you need this...
所以不要使用指针...使用(左值)引用:
So don't use pointers... use (lvalue) references:
这还没有解决你的任务吗?
但无论如何,对于这种情况,我推荐使用 std::auto_ptr 之类的东西。
Doesn't this solve your task?
But I anyway recommend something like std::auto_ptr for such cases.
C 或 C++ 无法让您确定内存分配的位置,即进入函数参数的位置。如果您想要更好的安全性,请使用 .NET 进行编程。
如果您想让它更安全,那么不仅仅是完全更改您的函数签名以接受自动指针。这样,语义就变得非常清晰,并且对于谁或什么拥有记忆就不会有任何混淆。
C or C++ does not give you the luxury of determining where the memory was allocated, that come into your function's parameters. If you want better safety then program in .NET.
If you want to make it safer, than just completely change your function signature to accept an auto pointer. that way the semantics become crystal clear, and there should be no confusion as to whom or what owns the memory.