#c #class #templates #pointers #declaration
#c #класс #шаблоны #указатели #объявление
Вопрос:
template <typename T>
class Node
{...};
int main
{
Node* ptr;
ptr = new Node<int>;
}
Не удастся скомпилировать, я должен объявить указатель как
Node<int>* ptr;
Почему я должен указывать тип при объявлении указателя, я еще не создал класс, почему компилятор должен знать, на какой тип он будет указывать. И разве невозможно создать универсальный указатель и впоследствии решить, какой тип я хочу ему присвоить.
Комментарии:
1. Пока это не появилось ни в одном ответе, и я не думаю, что оно заслуживает написания, но вот важный момент:
Node
это не тип. Это шаблон. Это правда, что это шаблон класса , но только такие вещи, какNode<int>
,Node<double>
илиNode<Node<void> >
, являются типами;Node
навсегда останется шаблоном, и есть только указатели на типы. Нет такого понятия, как указатель на шаблон.2. Это настолько важный момент, что он заслуживает того, чтобы быть в ответе, а не просто в комментарии.
3. Разве правильный синтаксис не должен быть
Node<int>* ptr = new Node<int>();
? Я думаю,()
должно присутствовать.4. @enthusiasticgeek это необязательно
Ответ №1:
Создание шаблонов разрешает типы во время компиляции. Когда вы присваиваете ему новый Node<int>
объект, указатель должен знать во время компиляции, к какому именно типу он относится.
Node<int>
и Node<std::vector>
может сильно отличаться в двоичных файлах (двоичный макет вашего объекта полностью изменяется в соответствии с параметром шаблона), поэтому нет никакого смысла иметь неразрешенный тип указателя на шаблон.
Сначала вы должны определить общий родительский класс для ваших узлов:
class NodeBase
{ ... }
template<typename ValueT>
class Node : public NodeBase
{
...
};
NodeBase* ptr;
Комментарии:
1. @Mike, тебе удалось переписать мой полный ответ. Вы уверены, что это было вашим намерением?
2. Все, что я сделал, это добавил обратные знаки вокруг
Node<int>
иNode<std::vector>
, чтобы отформатировать их как код, а не текст. Без этого форматирования предложение гласило «Узел и Node могут сильно отличаться», что не имело смысла.3. @Yola, я не совсем уверен, что ты здесь имеешь в виду? Вопрос не касался накладных расходов на вызовы виртуальных функций. У виртуальных функций нет альтернативы в определенной ситуации, а именно, когда дело доходит до разрешения функций во время выполнения.
Ответ №2:
Простой ответ заключается в том, что C использует (довольно) строгую статическую проверку типов. Node<int>
это совершенно не связанный тип с Node<double>
, и когда компилятор видит ptr->doSomething()
, он должен знать, следует ли вызывать Node<int>::doSomething()
или Node<double>::doSomething()
.
Если вам действительно нужна какая-то динамическая общность, где фактический тип, на который будет указывать ptr
, будет известен только во время выполнения, вам нужно определить базовый класс и наследовать от него. (Это довольно распространенная идиома для шаблона класса, производного от базы, отличной от шаблона, именно для того, чтобы общность в указателях могла быть разрешена во время выполнения.)
Ответ №3:
Почему я должен указывать тип при объявлении указателя, я еще не создал класс, почему компилятор должен знать, на какой тип он будет указывать.
Есть много вещей, которые вам разрешено делать с помощью указателя, просто чтобы перечислить очень немногие:
- вызовите одну из многих функций на основе типа указателя
- индексируйте его (в предположении, что он указывает на первые элементы в непрерывном массиве таких объектов)
- определите размер объекта, на который указывается
- передайте его в шаблон, где тип указателя является параметром
Чтобы компилятор мог сгенерировать код для выполнения этих действий эффективно, ему необходимо знать тип указателя. Если бы он отложил принятие решений до тех пор, пока не увидит тип указателя, тогда ему нужно было бы либо:
- скомпилируйте эффективный код для каждого возможного типа, который указатель может позже принять (создавая чрезвычайно раздутую программу), или
- создайте неэффективный код, который может обрабатывать все возможные типы с помощью некоторых наихудших вариантов пессимистичного неуклюжего поведения, или
- внедрите свою копию (компилятор) в свою программу на C , чтобы она могла завершить свою работу, когда получит необходимую информацию — это сделало бы каждую тривиальную программу огромной (и медленной)
И разве невозможно создать универсальный указатель и впоследствии решить, какой тип я хочу ему присвоить.
Вроде того… у вас есть много вариантов:
- используйте
void*
, но прежде чем он снова сможет осмысленно работать с типом, на который указано, вам нужно вручную привести его обратно к этому типу: в вашем случае это означает, что где-то записывается то, что было тогда, с отдельным кодом для каждой возможности - использование
boost::any<>
— очень похоже наvoid*
, но со встроенной безопасностью - использовать
boost::variant<>
— намного безопаснее и удобнее, но при создании указателя необходимо указать возможные типы, на которые указывается - используйте полиморфное семейство объектов среды выполнения и виртуальную отправку… это классическое объектно-ориентированное программирование… у вас есть указатель на «абстрактный»,
Node
который объявляет совместно используемые функции и данные-члены, которые вы использовали бы для работы с любым конкретным типом узла, затем шаблонныйNode
класс является производным от этого абстрактногоNode
и реализует версии функций, зависящие от типа. Затем они вызываются через указатель на базовый класс с использованиемvirtual
функций.
Ответ №4:
Всякий раз, когда вы создаете какой-либо объект (включая указатели) на C , должен быть известен полный тип объекта. В вашем коде нет такого типа, как Node
, поэтому вы не можете создавать экземпляры указателей на него. Вам нужно переосмыслить то, как вы разрабатываете и пишете свой код.
Ответ №5:
Как отметили Нил Баттерворт и Люк Дантон, у вас не может быть указателя типа Node *, потому что Node не является типом. Это шаблон. Да, Node является шаблоном класса, но этот класс здесь просто определяет тип шаблона. Один из способов взглянуть на это: точно так же, как классы и экземпляры классов — это очень разные вещи, так же как шаблоны и их экземпляры. Классами являются экземпляры шаблонов, такие как Node. Шаблон класса — это какой-то другой зверь.