Страницы

Поиск по вопросам

понедельник, 9 декабря 2019 г.

Передача по ссылке - тождественна ли передаче указателя?

#cpp #visual_cpp #dll


Читал не раз, что передача по ссылке - это просто передача указателя, который внутри
функции автоматически разыменовывается. Насколько это верно?

Например, я написал такую dll (в Visual Studio):

extern "C"
void __declspec(dllexport) test(int* i)
{
    cout << "test(" << *i << ")\n";
    (*i)++;
}


И вот такую программу для работы с ней:

typedef void (*myfunc)(int&);
int main()
{
    HMODULE dll;
    if ((dll = LoadLibrary("Dll.dll")) != NULL)
    {
        myfunc f = (myfunc)GetProcAddress(dll,"test");
        int q = 5;
        cout << "q = " << q << endl;
        f(q);
        cout << "q = " << q << endl;
        FreeLibrary(dll);
    }
}


Все работает, получается что я и хотел:

q = 5
test(5)
q = 6


Только насколько на эту передачу по ссылке как указателя можно полагаться? Какие
могут быть неприятности?

И еще сразу хотел спросить, у меня, например, в dll используется cout - это тот же
cout, что и в основной программе или нет? А если я передам на него указатель - то все
равно ведь будет использоваться код в dll, а не в программе? Или если я выделю в dll
память, то как ее правильно освободить в программе? например, если я верну unique_ptr
в функции в dll?
    


Ответы

Ответ 1



Нет, не тождественна. Указатель - это физический объект в памяти, занимающий место и умеющий значение. Ссылка - это просто синоним имени объекта, для хранения которого не выделяется дополнительной памяти. Другими словами, когда Вы оперируете ссылками, то можете представить что работаете с той же переменной, что и при объявлении (для компилятора нет разницы в этом случае). В случае использования динамически подключаемых библиотек, строгой рекомендацией является использование С интерфейса (в С существуют только указатели), дабы иметь возможность работать с библиотекой из других языков. Не стоит в данном случае возвращать std::unique_ptr<>. Также насчёт выделения памяти, если Вы выделяете память внутри библиотеки, то внутри и должны освобождать. Использование умных указателей в данном случае - современный подход слежения за памятью. Старайтесь строго избегать явных вызовов delete. Прежде всего стоит определиться нужно действительно нужно ли выделять память внутри библиотеки или допустимо передать ей буфер и его размер. Если всё же библиотеке нужно выделять память и возвращать её, то допустим мы имеем две функции: void* dll_alloc(size_t size) и dll_free(void* ptr) Тогда для удобной работы и автоматическим освобождением памяти, можем использовать следующее: std::shared_ptr dllMemory(dll_alloc(1024), dll_free); В таком случае, нам не нужно явно вызывать dll_free.

Ответ 2



Вы не имеете права предполагать, что ссылка внутри устроена как указатель. Декларация функции, отличающаяся от её настоящего определения, есть undefined behaviour. То, что это «работает» — случайность, как и при любом UB. С усложнением программы и/или изменением настроек компилятора возможны любые неприятные сюрпризы. То, что в приведённых вами словах верно — это то, что ссылка ведёт себя во многом как автоматически разыменуемый указатель. Важное семантическое отличие между указателем и ссылкой — ссылку нельзя переназначить, и она обязательно указывает на какой-то объект (в корректной программе). То есть, не существует «null-ссылок».

Ответ 3



По первой части - думаю, что в стандарте никаких указаний нет и не должно быть. Откровенно говоря, не думаю, что если такая передача в VC++ получается успешно, то в какой-то момент перестанет быть таковой... но вполне допускаю, что другой компилятор может работать и иначе. Поэтому, как мне кажется, в пределах одного компилятора - грубо говоря, в своей программе - пользоваться в принципе можно... отдавая отчет в том, что теоретически это может сломаться в каких-то будущих версиях. Но не проще ли использовать лишний уровень косвенности? Просто написать функцию-посредник, принимающую ссылку, но вызывающую функцию DLL с передачей ей адреса этой ссылки? По второй части - если вы использовали статические библиотеки, то да, cout у вас разные, если же динамический runtime - то, думаю, один и тот же, из динамических библиотек. По поводу памяти - насколько я помню советы опытных :), крайне желательно освобождать память там, где она была выделена. Но как с этим согласовать те же интеллектуальные указатели или что-то иное - простой и надежный способ в голову пока не приходит.

Комментариев нет:

Отправить комментарий