Я довольно новичок в C ++, я в основном использую Python. Я пытаюсь проверить тип переменной значения, хранящегося в объектах, над которыми я работаю. Я помню, что в Python была команда isinstance
где я мог бы использовать его как условие для запуска определенных команд, например, если следующее значение является строкой, сделать A, и если это int do B.
Есть ли способ быстро проверить, какой тип данных для переменной в C ++?
Пример:
В Python у меня был массив с математической операцией, каждый символ в поле
[3,"+",2]
когда я читаю массив, я отделяю int от строк с помощью команды isinstance
if isinstance(list[0],int):
aux1.append(list[0])
list=list[1:]
else:
if isinstance(lista[0],str):
aux2.append(list[0
list=list[1:]
теперь в C ++ мне нужно сделать что-то подобное, но на этот раз каждый символ находится в узле из связанного списка, и снова мне нужно отделить их, целые числа в связанном списке и строки в другом связанном списке
Похоже, что вы боретесь с тем, что C ++ является статически и (относительно) строго типизированным языком. Для обсуждения того, что на самом деле означает каждый из этих терминов, я обращаюсь к этот другой вопрос, как там объясняется, наверное, намного лучше, чем я мог.
Прежде всего, вы должны быть уверены, что вам действительно нужно делать вещи так, как вы пытаетесь в настоящее время. Не пытайтесь писать код в стиле Python.
Тем не менее, есть в основном два разных подхода, с помощью которых вы можете добиться поведения, аналогичного тому, что позволяет вам делать Python (динамическая типизация, утка и, следовательно, относительно слабая типизация):
Используйте встроенные в C ++ механизмы динамического типа. Поэтому вам нужно создать так называемый полиморфный базовый класс, это класс, который имеет по крайней мере одну виртуальную функцию-член (деструктор также работает, если у вас нет определенного интерфейса — чаще всего должен быть виртуальным, чтобы избежать неприятных вопросов). Краткий пример:
struct Value {
virtual void write_to(std::ostream &) const = 0;
virtual void read_from(std::istream &) = 0;
virtual ~Value() {} // Absolutely required!!!
};
struct Number : public Value {
int data;
void write_to(std::ostream & stream) const {
stream << "<Number " << data << ">";
}
void read_from(std::istream & stream) {
stream >> data; // Not the same format as write_to, shame on me
}
// Implicit destructor is fine
};
struct String : public Value {
std::string data;
void write_to(std::ostream & stream) const {
stream << "<String " << data.size() << " " << data << ">";
}
void read_from(std::istream & stream) {
stream >> data; // Not the same format as write_to, shame on me
}
};
Используя это теперь вы можете, например, хранить Value
чей фактический тип вы можете позволить пользователю решить:
std::vector<std::unique_ptr<Value>> values;
while (wantsToEnterMoreValues) {
std::string choice = ask("What type of value do you want to enter?");
std::unique_ptr<Value> value;
if (choice == "string") {
value = std::make_unique<String>();
} else if (choice == "number") {
value = std::make_unique<Number>();
} else {
// launch apocalypse
}
value->read_from(std::cin);
values.push_back(value);
}
Это легко расширяется на несколько типов. Обратите внимание, что для использования встроенной в C ++ динамической типизации вам нужно обходиться без семантики значений, но вместо этого полностью использовать семантику ссылок, либо с использованием реальных ссылок, либо (в большинстве случаев, когда право собственности должно быть передано, как в примере выше, в values
вектор) с помощью указателей.
dynamic_cast
Подход работает очень похоже на это, за исключением того, что вы используете информацию о типе среды выполнения более явно и не нуждаетесь в унифицированном интерфейсе (но у вас гораздо больше работы для поддержки вашего кода).
Использовать union
языковая особенность. Это стало действительно возможным только в C ++ 11, где члены объединения могут быть нетривиально выполнимыми:
enum class Type {
Number, String
};
struct Value {
Type type;
union {
std::string string;
int number;
};
Value(std::string const & s) : type(Type::String), string(s) {}
Value(int n) : type(Type::Number), number(n) {}
Value(Value const & v) : type(v.type) {
switch (type) {
case Type::Number: number = v.number; break;
case Type::String: new (&string) std::string(v.string); break;
default: break; // Launch nuclear missiles
}
}
~Value() {
switch (type) {
case Type::String: string.~std::string(); break;
default: break;
}
}
};
Как видите, это довольно много работы. При таком подходе вы можете использовать семантику значений, но не можете так же легко расширить Value
s для поддержки большего количества типов. Кроме того, из-за использования union
, ты собираешься потратить немного памяти.
Итог: вам нужно реализовать поведение самостоятельно, но вы можете сделать это именно так, как вы хотите, чтобы оно велось. Например: вы также можете реализовать операторы присваивания operator=(Value const &)
которые делают неявные преобразования типов. Вы также можете использовать реализации из увеличение, лайк boost::any
или же boost::variant
,
Я хотел бы сослаться на два ответа, которые я написал на этом сайте, на одну и ту же тему, возможно, они также полезны для вас:
Также немного кода на C, который важен, потому что он пытается решить ту же проблему: https://stackoverflow.com/a/35443434/1116364
Примечание: весь код в этом ответе написан прямо из памяти и не проверен. Таким образом, он служит только для демонстрации основных методов.
В отличие от Python, C ++ является строго типизированным языком. Это означает, что тип каждого объекта известен во время компиляции.
Сказав это, есть очень, очень расплывчатый аналог, который может применяться в некоторых обстоятельствах.
Если у вас есть указатель на объект, класс которого имеет хотя бы один виртуальный метод, dynamic_cast
либо преобразует его в указатель на запрошенный класс, либо в nullptr
, Это будет работать только тогда, когда наиболее производный объект, на который указывает указатель, однозначно включает оба класса в свою иерархию.