C ++: Как запретить функции принимать указатель, выделенный в строке?

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

Учитывая этот прототип функции:

void Foo(myClass* bar);

Я хочу предотвратить такое использование:

Foo(new myClass());

и вместо этого требуется ранее созданный объект:

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

or

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 будут проигнорированы, я уже знаю, что это ужасно. У меня нет выбора.


person Casey    schedule 15.08.2010    source источник
comment
Почему вы хотите это сделать? Насколько я знаю, общего способа узнать разницу нет.   -  person Dean Harding    schedule 16.08.2010
comment
Я не знаю, зачем вам это нужно, но я мог бы предположить, что вы не хотите, чтобы вызывающие абоненты думали, что ваш код освободит их для них. Если вы хотите гарантировать, что деструктор будет вызван, даже если они создадут его инстанс, вы можете взять ссылку на класс интеллектуального указателя вместо необработанного указателя.   -  person Merlyn Morgan-Graham    schedule 16.08.2010
comment
Так что тебе нужно? К тому же чудо. Макросы. Множество макросов.   -  person Joey Adams    schedule 16.08.2010
comment
Я не понял вопроса. Кто-нибудь может отредактировать и объяснить вопрос, пожалуйста   -  person mousey    schedule 16.08.2010


Ответы (5)


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

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

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

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

Это делается в умном указателе shared_ptr. у Boost1 один, а в 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
}

И это работает. Но я не знаю, зачем вам это нужно.


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

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 есть ошибка, и он принимает это:

foo(new int);

в обоих случаях, хотя и не должно. (Однако он не принимает new int().)

person GManNickG    schedule 15.08.2010
comment
Вы были шустрее, +1 ;-) но я все равно предпочитаю обертки для таких случаев. - person Roman Nikitchenko; 16.08.2010
comment
Однако предостережение: функция 'foo' теперь может изменить указатель ввода 'bar', поскольку он передается по ссылке. Изменение входного параметра на неконстантную ссылку указывает разработчикам кода, что можно изменять входной параметр. Так что интерфейс функции теперь вводит в заблуждение ... - person Chubsdad; 16.08.2010
comment
@chubsdad: Ты хоть ответ читал? Вторая половина обращается к этому. - person GManNickG; 16.08.2010
comment
@chub: Нет проблем, я просто перечитал свой комментарий и увидел, насколько это означало, я собирался бросить туда улыбку, например: Ты вообще читал ответ? :) ... Извините. - person GManNickG; 16.08.2010
comment
@Gman: Теперь я понимаю, что должен сообщить своим пользователям, что им следует использовать RTFM :) (Хорошо, что я разрабатываю Справочное руководство по API одновременно с написанием этого движка!) +1. - person Casey; 16.08.2010
comment
@Casey: Действительно, в конечном итоге пользователь должен знать, что делает ваша функция. Но подумайте об использовании shared_ptr какого-либо типа или, по крайней мере, о создании своего собственного. Вы никогда не должны удалять вещи вручную. - person GManNickG; 16.08.2010
comment
@GMan: К сожалению, я не могу использовать shared_ptr. Движок основан на библиотеке Allegro, а библиотека Allegro не знает, что такое shared_ptr. - person Casey; 16.08.2010
comment
@Casey: Ну, вы должны сделать или использовать один, и вы можете использовать .get(), чтобы получить необработанный указатель. - person GManNickG; 16.08.2010
comment
@GManNickG: Я не думаю, что MSVC, разрешающий неконстантные ссылки на временные библиотеки, является ошибкой, а является предполагаемым расширением языка. - person porges; 25.09.2012

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

person pagra    schedule 15.08.2010

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

void Foo(myClass& bar);
person Sadeq    schedule 15.08.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;
}
person Roman Nikitchenko    schedule 15.08.2010

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

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

person C Johnson    schedule 16.08.2010