Есть ли законное использование void*
в C ++? Или это было введено, потому что это было в C?
Просто резюмирую свои мысли:
Ввод : если мы хотим разрешить несколько типов ввода, мы можем перегрузить функции и методы, в качестве альтернативы мы можем определить общий базовый класс или шаблон (спасибо, что упомянули это в ответах). В обоих случаях код становится более наглядным и менее подверженным ошибкам (при условии, что базовый класс реализован разумным образом).
Вывод : я не могу представить себе ситуацию, когда я предпочел бы получать, void*
а не что-то производное от известного базового класса.
Просто чтобы прояснить, что я имею в виду: я специально не спрашиваю, есть ли вариант использования void*
, но есть ли случай, когда void*
это лучший или единственный доступный выбор. На что несколько человек ниже прекрасно ответили.
variant
,any
, помеченный союз. Все, что может сказать вам реальный тип контента и что безопаснее в использовании.Ответы:
void*
по крайней мере необходимо как результат::operator new
(также каждыйoperator new
...) and ofmalloc
и как аргументnew
оператора размещения .void*
можно рассматривать как общий супертип для каждого типа указателя. Так что это не совсем указательvoid
, а указатель на что угодно.Кстати, если вы хотите сохранить некоторые данные для нескольких несвязанных глобальных переменных, вы можете использовать их
std::map<void*,int> score;
тогда, после того, как объявили globalint x;
anddouble y;
andstd::string s;
doscore[&x]=1;
andscore[&y]=2;
andscore[&z]=3;
memset
хочетvoid*
адрес (самые общие)Кроме того, в системах POSIX есть dlsym, и его тип возврата, очевидно, должен быть
void*
источник
char *
тоже можно вернуть . Они очень близки в этом аспекте, хотя смысл немного другой.C++
вC++
этом написано, это достаточная причина для того, чтобы иметьvoid*
. Должен любить этот сайт. Задайте один вопрос, узнайте много нового.void*
тип. Использованы все стандартные библиотечные функцииchar*
Есть несколько причин для использования
void*
, из которых 3 наиболее распространенные:void*
в ее интерфейсеВ обратном порядке обозначение нетипизированной памяти с помощью
void*
(3) вместоchar*
(или вариантов) помогает предотвратить случайную арифметику указателя; доступно очень мало операций,void*
поэтому обычно требуется преобразование, прежде чем оно станет полезным. И, конечно же, как и в случаеchar*
с псевдонимом, нет проблем.Стирание типа (2) все еще используется в C ++ в сочетании с шаблонами или нет:
std::function
И, очевидно, когда интерфейс, с которым вы работаете, использует
void*
(1), у вас мало выбора.источник
О да. Даже в C ++ иногда мы используем,
void *
а неtemplate<class T*>
потому, что иногда дополнительный код из расширения шаблона слишком много весит.Обычно я использовал бы его как фактическую реализацию типа, а тип шаблона унаследовал бы от него и обернул бы приведение типов.
Кроме того, должны использоваться специальные распределители slab (новые реализации операторов)
void *
. Это одна из причин, по которой g ++ добавил расширение, разрешающее арифметику указателя,void *
как если бы он был размером 1.источник
struct wrapper_base {}; template<class T> struct wrapper : public wrapper_base {T val;} typedef wrapper* like_void_ptr;
это минимальный симулятор void - * - с использованием шаблонов.Правда.
Отчасти это правда: что, если вы не можете определить общий базовый класс, интерфейс или что-то подобное? Чтобы определить их, вам нужен доступ к исходному коду, что часто невозможно.
Вы не упомянули шаблоны. Однако шаблоны не могут помочь вам с полиморфизмом: они работают со статическими типами, то есть известными во время компиляции.
void*
можно рассматривать как наименьший общий знаменатель. В C ++ он обычно не нужен, потому что (i) вы сами по себе мало что можете с ним сделать и (ii) почти всегда есть лучшие решения.Более того, вы, как правило, преобразуете его в другие конкретные типы. Вот почему
char *
обычно лучше, хотя это может указывать на то, что вы ожидаете строку в стиле C, а не чистый блок данных. Вот почемуvoid*
это лучше, чемchar*
для этого, потому что он позволяет неявное приведение из других типов указателей.Вы должны получать данные, работать с ними и производить результат; для этого вам необходимо знать данные, с которыми вы работаете, иначе у вас возникнет другая проблема, отличная от той, которую вы решали изначально.
void*
Например, у многих языков нет и нет проблем с этим.Другое законное использование
При печати адресов указателя с помощью таких функций, как
printf
указатель, должен бытьvoid*
тип, и поэтому вам может потребоваться приведение кvoid
*источник
Да, это так же полезно, как и все остальное на этом языке.
Например, вы можете использовать его, чтобы стереть тип класса, который вы можете статически преобразовать к нужному типу при необходимости, чтобы иметь минимальный и гибкий интерфейс.
В этом ответе есть пример использования, который должен дать вам представление.
Я скопировал и вставил его ниже для ясности:
class Dispatcher { Dispatcher() { } template<class C, void(C::*M)() = C::receive> static void invoke(void *instance) { (static_cast<C*>(instance)->*M)(); } public: template<class C, void(C::*M)() = &C::receive> static Dispatcher create(C *instance) { Dispatcher d; d.fn = &invoke<C, M>; d.instance = instance; return d; } void operator()() { (fn)(instance); } private: using Fn = void(*)(void *); Fn fn; void *instance; };
Очевидно, что это только одно из множества применений
void*
.источник
Взаимодействие с функцией внешней библиотеки, которая возвращает указатель. Вот один для приложения на Аде.
extern "C" { void* ada_function();} void* m_status_ptr = ada_function();
Это возвращает указатель на то, о чем Ада хотела вам рассказать. Вам не нужно делать с ним ничего особенного, вы можете вернуть его Аде, чтобы он сделал следующее. На самом деле распутывание указателя Ada в C ++ нетривиально.
источник
auto
вместо этого в этом случае? Предполагая, что тип известен во время компиляции.Короче говоря, C ++ как строгий язык (без учета таких реликвий C, как malloc () ) требует void *, поскольку у него нет общего родителя для всех возможных типов. В отличие от ObjC, например, у которого есть object .
источник
malloc
иnew
оба возвращаютсяvoid *
, так что вам нужно было бы, если бы даже был класс объекта в C ++operator new()
возвращаетсяvoid *
, аnew
выражение - нетint
2. если это не виртуальный EBC, то чем он отличается отvoid*
?Первое, что приходит мне в голову (что, как я подозреваю, является конкретным случаем пары ответов, приведенных выше), - это возможность передать экземпляр объекта в threadproc в Windows.
У меня есть пара классов C ++, которым это необходимо, у них есть реализации рабочего потока, а параметр LPVOID в API CreateThread () получает адрес реализации статического метода в классе, чтобы рабочий поток мог выполнять работу с конкретный экземпляр класса. Простое статическое приведение обратно в threadproc дает экземпляр для работы, позволяя каждому созданному объекту иметь рабочий поток из единственной реализации статического метода.
источник
В случае множественного наследования, если вам нужно получить указатель на первый байт блока памяти, занятого объектом, вы можете это
dynamic_cast
сделатьvoid*
.источник