C ++: Как я могу запретить функции принимать указатель, который размещен в строке? - PullRequest
5 голосов
/ 16 августа 2010

Не могу понять, как правильно сформулировать вопрос, вот пример:

Для данного прототипа функции:

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;
}

Любое изображение, которое пользователь передает, копируется в изображение объекта.

Если я освобождаю переданное изображение после его копирования, и изображение используется в другом местев программе произойдет сбой программы с нарушением прав доступа.

Если они выделяют хранилище in-line, а я не освобождаю его, происходит утечка памяти.Проблема усугубляется, если они вызывают метод SetImage несколько раз в течение работающей программы.

Комментарии об использовании альтернативных библиотек или в самой библиотеке Allegro будут игнорироваться, я уже знаю, что это ужасно.У меня нет выбора.

Ответы [ 5 ]

19 голосов
/ 16 августа 2010

Ваш дизайн должен сделать выбор. Либо вступите во владение и удалите его, либо не вступайте во владение. В любом случае, пользователь должен знать, как использовать вашу функцию. Они должны либо знать, что ваша функция уничтожит изображение (и, возможно, передать свою собственную копию при необходимости), либо они должны достаточно умен, чтобы управлять своими ресурсами.

Как правило, вы не хотите украсть собственность, просто чтобы удалить ее. Так что я бы не удалил что-нибудь. Если кто-то настолько глуп, чтобы потерять способность удалять передаваемое изображение, проблема не в этой функции. Другими словами, вы должны попытаться защититься от Мерфи, но забудьте о защите от Макиавелли.

Тем не менее, использование сырых указателей плохо! Плохой код C ++ отмечен ручным управлением ресурсами и проблемами с ресурсами. Вы должны иметь обертку вокруг изображения, которая удалит изображение в деструкторе. Таким образом, вы не можете никогда утечка, даже если выдается исключение. Предоставьте ему метод reset(), который отбрасывает его старый ресурс изображения и получает новый.

Звучит так, как будто вы хотите совместно владеть, поэтому вам нужна оболочка с подсчетом ссылок. Затем проблема решается: если кто-то делает «встроенное» выделение, оно будет помещено в общий указатель, а затем автоматически удалено, когда это будет сделано. (И еще лучше иметь конструктор explicit, чтобы кто-то знал, что он будет делиться ресурсом.)

Это делается с помощью умного указателя под названием shared_ptr. Boost имеет один, TR1 - один, а C ++ 0x - один. Просто дайте ему пользовательское удаление (которое освобождает изображение), и вы больше никогда не будете беспокоиться об управлении ресурсами.

Это должно быть сделано с всеми ресурсами. Концепция здесь: Управление ресурсами с привязкой к области действия (SBRM); что ресурс управляется автоматически, используя правила жизни автоматических (стековых) переменных. Он известен как оригинальное, но более уродливое имя Ресурс-Приобретение Инициализация (RAII). Проведите небольшое исследование в этой области, и вы обнаружите, что ваш код проще и чище.


Это не может быть сделано без изменения типа параметра. Вы можете изменить его на:

void Foo(myClass*& bar);

Поскольку неконстантная ссылка может быть привязана только к lvalue:

void foo(int*&);

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

    foo(i); // well-formed
    foo(&j); // ill-formed
    foo(new int); // ill-formed
}

Однако это не позволяет принимать адрес lvalue. Конечно, вы можете сделать простое:

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().)

4 голосов
/ 16 августа 2010

Невозможно иметь такое различие в использовании. И во всех случаях это допустимый параметр. Я действительно не могу понять, зачем тебе это нужно ...

2 голосов
/ 16 августа 2010

Разве это не решает вашу задачу? Но я в любом случае рекомендую что-то вроде 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;
}
2 голосов
/ 16 августа 2010

Так что не используйте указатели ... используйте (lvalue) ссылки:

void Foo(myClass& bar);
0 голосов
/ 16 августа 2010

C или C ++ не дают вам роскоши определять, где была выделена память, которая входит в параметры вашей функции.Если вы хотите большей безопасности, то программируйте на .NET.

Если вы хотите сделать это безопаснее, чем просто полностью измените сигнатуру своей функции, чтобы принять автоматический указатель.таким образом семантика становится кристально чистой, и не должно быть никакого замешательства относительно того, кому или что принадлежит память.

...