Должен ли я использовать Dynamic_cast<T> для копирования?
-
06-09-2019 - |
Вопрос
Обновление 1:
Исправлен бредовый код!Спасибо за комментарии, я сделал хеш из первого фрагмента, упс.
Обновление 2:
Также обновлен заголовок вопроса, поскольку в ответах было указано, что использование Dynamic_cast не является необходимым.
Здесь я пытаюсь добиться глубокого копирования с использованием строгих типов;Я хочу иметь возможность скопировать Class2 в другой экземпляр Class2;однако я также хочу использовать функцию CopyTo из класса Class1, который является базовым.Эта идея исходит из моего опыта работы с C#, когда обычно я просто делал тип возвращаемого значения универсальным (см. фрагмент кода C#).
void Class1::CopyTo(Class1 *c1)
{
// Write data in to c1 from this instance.
c1->exampleData = exampleData;
}
// Class2 inherits Class1
Class2 *Class2::Copy()
{
Class2 *c2a = new Class2();
CopyTo(c2a);
Class2 *c2b = dynamic_cast<Class2*>(c2a);
return c2a;
}
И вот как я это сделал бы на C#:
public class Class1
{
T Copy<T>()
where T : Class1
{
/* Can't remember the best way to do this in C#;
* basically if T was Class2 this would need to create
* a new instance of that class, and the same goes for
* Class1. */
T copy = createNewInstance();
// Copy the data from this to 'copy'.
copy.exampleData = exampleData;
return copy;
}
}
Теперь, по сравнению с фрагментом C#, фрагмент C++ кажется вонючим.Можно ли сделать это без указателей или это лучшая практика?
Решение
Я не уверен, чего вы пытаетесь достичь, потому что код все еще не имеет особого смысла.Однако я считаю, что следующее должно приблизительно соответствовать тому, что вы пытаетесь сделать.Обратите внимание, что я не использовать кучу памяти:в этом нет необходимости, и это приведет к утечке памяти.
template <typename T>
T Class1::Copy()
{
T instance;
CopyTo(&instance);
return instance;
}
Это работает, потому что вы передаете (полиморфный) указатель на instance
к CopyTo
метод Class1
.
Тогда вы можете вызвать код следующим образом:
Class2 x1;
// Fill x1
Class2 x2 = x1.Copy<Class2>();
Однако этот код все еще пахнет, потому что это не идиоматический C++:В C++ вместо этого обычно пишут конструктор копирования.Позднее прибытие Copy
методы существуют, но они необходимы очень редко, и приведенное выше не является поздней привязкой (но и ваш код C# тоже).
Другие советы
Я не понимаю, о чем вы спрашиваете, но имейте в виду, когда вы говорите:
Class2 *c2 = dynamic_cast<Class2*>(c1);
результат приведения может быть NULL, и вы должны это проверить.
Вам следует еще немного поработать над фрагментом кода.GetSomethingCopy создает указатель типа Class2, который передается в CopyTo.CopyTo пытается вызвать функцию-член полученного указателя, который никогда не был инициализирован:ошибка сегментации и программа умирает.
Даже если это не убило приложение, вы пытаетесь динамический_cast из класса 2* в класс 2*, что практически ничего не делает.Если вы намереваетесь привести возвращаемое значение из CopyTo, вы должны знать, что вы не можете использовать Dynamic_cast для void*.Вы должны либо изменить подпись CopyTo, чтобы вернуть Class1 (чтобы вы могли позже его привести), либо использовать static_cast для void*.
Обратите внимание, что либо Copy — это виртуальная функция в классе 1, которая фактически выполняется в классе 2 и создает объект класса 2, либо возвращаемый элемент будет не классом 2, а скорее классом 1.
Название метода CopyTo сбивает с толку, поскольку оно не копирует к аргумент, а скорее из аргумента.
И после всего этого я до сих пор не понимаю, о чем вы спрашиваете.Где бы вы хотели использовать стековую память?Вы можете передать элемент, выделенный в стеке, в функцию, но возврат указателя/ссылки на элемент, выделенный в стеке, снова является ошибкой сегментации:объект будет уничтожен, когда функция завершится, и у получателя останется висячий указатель/ссылка.
Теперь, если ваш вопрос более теоретический о том, можете ли вы использовать Dynamic_cast для элемента, выделенного в стеке, вы можете (при условии, что Class2 наследует от Class1):
void f()
{
Class2 c2;
Class1 &c1 = c2; // c1 is a Class1 reference to a Class2 object
dynamic_cast<Class2&>(c1).class2method();
// or:
dynamic_cast<Class2*>(&c1)->class2method();
}
Если вы обновите код, оставьте комментарий в этом ответе, чтобы я заметил и исправил его сегодня вечером.
этот код не имеет смысла, как указано...в любом случае, я «думаю», вы могли бы использовать статическое приведение, если бы вы не использовали void* в качестве возврата?
Хорошо, теперь код имеет смысл.
Вам не нужно никакого динамического приведения, оно уже имеет тип Class2.
В вашей функции CopyTo вы будете возвращать указатель на объект, который был создан в стеке — это невозможно, поскольку объект, на который указывает указатель, будет уничтожен при возврате функции.
В ответ на ваш вопрос вы можете использовать dynamic_cast
по указателю или ссылке.В вашем случае я мог бы выделить объект, который будет возвращен динамически, используя new
а не в стеке, то вы можете безопасно вернуть указатель.Однако я склонен использовать dynamic_cast
как потенциальный запах кода и признак того, что следует использовать виртуальную функцию.
Нет, динамический_cast работает только с указателями и ссылками.В любом случае вы не сможете безопасно вернуть все, что разместили в стеке, поэтому я не уверен, как вы собирались изменить свой код в этом случае.
Ах, теперь проблема ясна.Технически ответ — нет, не с помощью Dynamic_cast<>, но я действительно не понимаю, зачем вам это нужно.Кажется, ты просто хочешь
void Class1::CopyTo(Class1& c1)
{
// Write data in to c1 from this instance.
c1.SomeIntValue = SomeIntValue;
}
// Class2 inherits Class1
Class2* Class2::Copy()
{
Class2 *c2 = new Class2();
CopyTo(*c2);
return c2;
}
//or more idiomatic
Class2 Class2::Copy()
{
Class2 c2
CopyTo(c2);
return c2;
}