Единственными определяемыми классом функциями будут стандартный конструктор, функции-члены управления копированием и ряд операторов присвоения, способных присваивать значение одного из типов объединения члену другого:
class Token {
public:
//
//
//
//
Token(): tok(INT), ival{0} { }
Token(const Token &t): tok(t.tok) { copyUnion(t); }
Token &operator=(const Token&);
//
//
~Token() { if (tok == STR) sval.~string(); }
//
Token &operator=(const std::string&);
Token &operator=(char);
Token &operator=(int);
Token &operator=(double);
private:
enum {INT, CHAR, DBL, STR} tok; //
union { //
char cval;
int ival;
double dval;
std::string sval;
}; //
//
//
void copyUnion(const Token&);
};
Класс определяет вложенное, безымянное перечисление с не ограниченной областью видимости (см. раздел 19.3), используемое как тип члена tok
. Член tok
определен после закрывающей фигурной скобки и перед точкой с запятой, завершающей определение перечисления, которое определяет tok
, как имеющий тип этого безымянного перечисления (см. раздел 2.6.1).
Член tok
будет использован как дискриминант. Когда объединение содержит значение типа int
, член tok
будет содержать значение INT
; если объединение содержит значение типа string
, то член tok
содержит значение STR
и т.д.
Стандартный конструктор инициализирует дискриминант и член объединения как содержащие значение 0
типа int
.
Поскольку объединение содержит член, класс которого обладает деструктором, следует определить собственный деструктор, чтобы (условно) удалять член типа string
. В отличие от обычных членов типа класса, члены типа класса, являющиеся частью объединения, не удаляются автоматически. У деструктора нет никакого способа узнать, значение какого типа хранит объединение. Таким образом, он не может знать, какой из членов следует удалить.
Поэтому деструктор проверяет, не содержит ли удаляемый объект строку. Если это так, то деструктор явно вызывает деструктор класса string
(см. раздел 19.1.2) для освобождения используемой памяти. Если объединение содержит значение любого из встроенных типов, то деструктор не делает ничего.
Операторы присвоения устанавливают значение переменной tok
и присваивают соответствующий член объединения. Подобно деструктору, эти функции-члены должны условно удалять строку, прежде чем присваивать новое значение объединению:
Token &Token::operator=(int i) {
if (tok == STR) sval.~string(); //
ival = i; //
tok = INT; //
return *this;
}
Если текущим значением объединения является строка, ее следует освободить прежде, чем присвоить объединению новое значение. Для этого используется деструктор класса string
.
Как только член типа string
освобождается, предоставленное значение присваивается члену, тип которого соответствует типу параметра оператора. В данном случае параметр имеет тип int
, поэтому он присваивается ival
. Затем обновляется дискриминант и осуществляется выход.
Операторы присвоения для типов double
и char
ведут себя, как и версия для типа int
, их определение остается в качестве самостоятельного упражнения. Версия для типа string
отличается от других, поскольку она должна управлять переходом от типа string
и к нему: