Является ли void * необходимым, кроме вещей, связанных с выделением памяти - PullRequest
5 голосов
/ 04 сентября 2011

Нужно ли void* помимо вещей, связанных с выделением памяти в C ++?Можете привести пример?

Ответы [ 6 ]

5 голосов
/ 04 сентября 2011

Регистрация адресов памяти

Если вы хотите вывести указатель с помощью iostreams (например, для ведения журнала), то использование void* - это единственный способ убедиться, что operator<< не был перегружен каким-то безумным способом.

#include <iostream>

struct foo {
};

std::ostream& operator<<(std::ostream& out, foo*) {
  return out<<"it's a trap!";
}

int main() {
  foo bar;
  foo *ptr = &bar;

  std::cout << ptr << std::endl;
  std::cout << static_cast<void*>(ptr) << std::endl;
}

Проверка состояния iostream

iostreams overload operator void* в качестве проверки состояния, поэтому такой синтаксис, как if (stream) или while (stream), является кратким способом проверки состояния потока.


Шаблон метапрограммирования

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


Доступ к наиболее производному указателю

Как отметил Альф в комментариях dynamic_cast<void*> также полезно для получения наиболее производного типа в иерархии, например ::1010 *

#include <iostream>

struct other {
  virtual void func() = 0;
  int c;
};

struct foo {
  virtual void func() { std::cout << "foo" << std::endl; }
  int a;
};

struct bar : foo, other {
  virtual void func() { std::cout << "bar" << std::endl; }
  int b;
};

namespace {
  void f(foo *ptr) {
    ptr->func();
    std::cout << ptr << std::endl;
    std::cout << dynamic_cast<void*>(ptr) << std::endl;
  }

  void g(other *ptr) {
    ptr->func();
    std::cout << ptr << std::endl;
    std::cout << dynamic_cast<void*>(ptr) << std::endl;
  }
}

int main() {
  foo a;
  bar b;
  f(&a);
  f(&b);
  g(&b);
}

Дает:

foo
0xbfb815f8
0xbfb815f8
bar
0xbfb815e4
0xbfb815e4
bar
0xbfb815ec
0xbfb815e4

В моей системе.


Исключения

§ 15.3.1 заявляет:

Объявление-исключение не должно обозначать указатель или ссылку на неполный тип, кроме void *, const void*, volatile void * или const volatile void *.

Так что, похоже, единственный допустимый способ перехватить указатель на неполный тип - это через void*. (Хотя я думаю, что, возможно, есть большие проблемы, если вам действительно нужно это использовать)


Legacy C использует

Существует множество "устаревших" C, использующих void* для хранения указателей на данные, не зная, что это такое, но в новом коде C ++ почти всегда есть лучший способ выразить ту же функциональность.

2 голосов
/ 04 сентября 2011

void* часто используется для обратных вызовов.

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

[ отсюда ]

Конечно, это небезопасно, поэтому люди придумывают способы обернуть подобные вещи, как здесь .

2 голосов
/ 04 сентября 2011

внешние библиотеки используют их часто (особенно в C).

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

1 голос
/ 04 сентября 2011

void * используется в C ++ для выражения типа указателя на неизвестную структуру.

Поэтому я буду использовать его всякий раз, когда у меня есть тип указателя, где код не должен знать, что там находится:

  • выделение памяти
  • контейнеры
  • ...

Часто void * очень хорошо смешивается с шаблоном кода, чтобы избежать раздувания кода, вызванного шаблоном.

представьте, что вы реализуете

template <typename T>
class vector {
 /*stuff */
};

затем вы можете создать шаблонную специализацию для T *, которая использует указатели void, чтобы код не дублировался.

1 голос
/ 04 сентября 2011

Одна из ситуаций, когда вам может понадобиться использовать void, - при передаче буфера данных, например, в этой функции:

void loadData(void* data, std::size_t size)

Большая часть кода передает буфера через указатель на символ вместо void-указатель, потому что, как правило, буферы считываются в 1-байтовых чанках, что соответствует размеру, который стандарт C ++ обеспечивает для char.

Однако использование void-указателей является более общим.Это способ для функции сказать вам: «Просто дайте мне некоторые данные, не беспокойтесь о том, как я их прочитаю».Затем функция может привести указатель и прочитать данные кусками или любым другим размером.

1 голос
/ 04 сентября 2011

Нет, это не так. Это просто идиома для обозначения нетипизированной памяти

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...