#c
#c
Вопрос:
Я работаю в проекте с огромной базой устаревшего кода и пытаюсь перепроектировать его части, чтобы уйти от старого кода в стиле C.
Я столкнулся с проблемой и подготовил короткую программу для объяснения.
Используемый мной устаревший интерфейс требует, чтобы я передавал указатель на результирующие данные как void *, и я хотел бы избежать необходимости изменять это.
unique_ptr в примере просто демонстрирует, что в моей реальной кодовой базе все, что работает с данными, использует интеллектуальные указатели для управления памятью.
Моя проблема в том, что результирующие данные нарушаются (см. Последнюю строку вывода / последний вызов printPayload); в конце все равно 0, но, похоже, это не проблема с преобразованием в void * и обратно, как показано 2-й и 3-й строками вывода.
Это проблема, связанная с временными? Я не понимаю…
Я надеюсь, что такого рода проблема актуальна для некоторых из вас.
#include <iostream>
#include <memory>
struct Payload
{
long a;
int b;
int c;
Payload() : a(), b(), c() {}
Payload(long setA, int setB, int setC) : a(setA), b(setB), c(setC) {}
};
void printPayload(const Payloadamp; printThis)
{
std::cout << "payload -- a: " << printThis.a << " b: " << printThis.b << " c: " << printThis.c << std::endl;
}
void doSomething(Payload* sourceData, void* targetData)
{
if (!sourceData) return;
std::unique_ptr<Payload> sourceDataUnique(sourceData);
sourceDataUnique->a = 222;
sourceDataUnique->b = 333;
sourceDataUnique->c = 444;
printPayload(*sourceDataUnique);
targetData = reinterpret_cast<void*>(sourceDataUnique.release());
printPayload(*(reinterpret_cast<Payload*>(targetData)));
}
int main(void)
{
Payload* myPayload = new Payload(14, 8, 1982);
Payload myResu<
printPayload(*myPayload);
doSomething(myPayload, amp;myResult);
printPayload(myResult);
}
Вывод:
payload -- a: 14 b: 8 c: 1982
payload -- a: 222 b: 333 c: 444
payload -- a: 222 b: 333 c: 444
payload -- a: 0 b: 0 c: 0
Комментарии:
1. Вы установили
targetData
вdoSomething
, но это изменение локально для функции. Параметр должен быть ссылкой.2. Это можно упростить до:
void doSomething(Payload *src, Payload *dst) { dst = src; }
3. @PankajDaga, это не сработает, поскольку это будет ссылка на временное.
4. Прекратите использовать
reinterpret_cast
таким образом. Это самая сильная из существующих форм приведения, и вам не нужно использовать кувалду для преобразования изPayload*
вvoid*
и обратно.5. извините за это приведение. Я думаю, static_cast подойдет идеально. Однако я чувствую, что static_cast будет выглядеть бессмысленным в контексте. где типы не имеют необходимой связи. Я использовал reinterpret_cast . чтобы отметить принудительное отношение типа. Я думаю, вы этого не делаете…
Ответ №1:
targetData
является локальной переменной для doSomething
. После того, как вы назначаете ему адрес, он выходит за рамки.
На самом деле вы никогда не присваиваете myResult
.
Ответ №2:
В вашем коде параметр targetData
является локальным для doSomething
функции (т. Е. Любое изменение теряется после выхода из области действия функции). Однако *targetData
ссылается на переменную, myResult
объявленную в main
функции.
Итак, следующий код должен работать:
void doSomething(Payload* sourceData, void* targetData)
{
if (!sourceData) return;
sourceData->a = 222;
sourceData->b = 333;
sourceData->c = 444;
printPayload(*sourceData);
Payload* td = static_cast<Payload*>(targetData);
*td = *sourceData;
printPayload(*td);
}
Комментарии:
1. @melpomene конечно. Это была опечатка. Собираюсь отредактировать исходный код. Спасибо.
2. @JonathanWakely да, я просто хотел сохранить функцию, максимально похожую на исходный код. Собираюсь отредактировать исходный код.
Ответ №3:
Вы никогда не копируете исходные данные в цель, а только изменяете, на какой объект указывает указатель targetData.
Что-то вроде этого сработало бы:
Payload* targetPayload = reinterpret_cast<Payload*>(targetData);
*targetPayload = *sourceData;
Передача права собственности на исходную полезную нагрузку с помощью интеллектуального указателя, вероятно, является плохой идеей — если вызывающий код написан для правильной обработки исключений, то он удалит объект при ошибке, поэтому интеллектуальный указатель будет означать, что он удаляется дважды. Если вызывающий код написан неправильно для обработки исключений, то ваша задача — написать код, который не может генерировать исключение, с которым интеллектуальный указатель не помогает.
(поскольку это преобразование между указателями, вы могли бы использовать static_cast , но я предпочитаю reinterpret_cast, поскольку void * может быть чем угодно, а reinterpret_cast сообщает другим разработчикам, что происходит что-то потенциально опасное.)