【问题标题】:C++: How do I prevent a function from accepting a pointer that is allocated in-line?C++:如何防止函数接受内联分配的指针?
【发布时间】:2010-08-15 20:39:21
【问题描述】:

不知道如何准确地表达问题,所以这里有一个例子:

给定这个函数原型:

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 库本身的评论将被忽略,我已经知道这很糟糕。我别无选择。

【问题讨论】:

  • 您为什么要这样做?据我所知,没有一般的方法可以知道其中的区别。
  • 我不知道你为什么想要这个,但我可以假设你不希望调用者认为你的代码会为他们解除分配。如果你想保证即使他们内联实例化它,析构函数也会被调用,你可以引用一个智能指针类,而不是一个原始指针。
  • 那么你需要什么?除了奇迹。宏。很多宏。
  • 我没听懂这个问题。有人可以编辑并解释问题吗

标签: c++ pointers parameters function allegro


【解决方案1】:

您的设计需要做出选择。要么取得所有权并将其删除,要么不取得所有权。 无论哪种方式,由用户决定如何使用你的函数。他们要么需要知道你的函数会破坏图像(并且可能根据需要传递他们自己的副本),要么他们需要足够聪明地管理自己的资源。

通常,您不想为了删除所有权而窃取所有权。所以我不会删除任何东西。如果有人愚蠢到失去删除他们传递的图像的能力,那不是这个功能问题。换句话说,您应该尝试防御墨菲,但忘记防御马基雅维利。

也就是说,使用原始指针很糟糕!糟糕的 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*>);

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


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

foo(new int);

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

【讨论】:

  • 你更快,+1 ;-) 但无论如何我更喜欢这种情况的包装器。
  • 然而需要注意的是:函数'foo'现在有可能改变输入指针'bar',因为它是通过引用传递的。将输入参数更改为非常量引用向代码的维护者表明,可以修改输入参数。所以现在函数的界面变得误导......
  • @chubsdad:你读过答案吗?后半部分解决了这个问题。
  • @chub:没问题,我只是重新阅读了我的评论,发现这听起来很刻薄,我打算在那里微笑:“你有没有阅读答案?: ) ...”对不起。
  • @Gman:我现在明白了,我必须通知我的用户他们应该使用 RTFM :) (幸好我正在开发 API 参考手册来编写这个引擎!)+1。跨度>
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    void Foo(myClass& bar);
    

    【讨论】:

      【解决方案4】:

      这不能解决您的任务吗? 但无论如何,对于这种情况,我还是推荐 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】:

        C 或 C++ 无法让您确定分配内存的位置,即进入函数参数的位置。如果您想要更好的安全性,请使用 .NET 进行编程。

        如果你想让它更安全,不仅仅是完全改变你的函数签名来接受自动指针。这样,语义就变得非常清晰,不应该混淆谁或什么拥有记忆。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-02-11
          • 1970-01-01
          • 1970-01-01
          • 2016-12-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多