【发布时间】:2021-10-22 21:48:21
【问题描述】:
我有这个来自 C++ 入门第 5 版的文本。 ch 19.6 联合:
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; // discriminant union { // anonymous union char cval; int ival; double dval; std::string sval; }; // each Token object has an unnamed member of this unnamed union type // check the discriminant and copy the union member as appropriate void copyUnion(const Token&); }; Token &Token::operator=(const std::string &s) { if (tok == STR) // if we already hold a string, just do an assignment sval = s; else new(&sval) string(s); // otherwise construct a string tok = STR; // update the discriminant return *this; }在这种情况下,如果联合已经保存了一个字符串,我们可以使用普通的字符串赋值运算符为该字符串赋予一个新值。否则,不存在可以调用字符串赋值运算符的现有字符串对象。相反,我们必须在保存联合的内存中构造一个字符串。我们使用placement new (§ 19.1.2, p. 824) 在
sval所在的位置构造一个字符串。我们将该字符串初始化为字符串参数的副本。我们接下来更新判别式并返回。
一切对我来说都是那么简单,但让我感到困惑的是这个版本的复制赋值运算符(它需要一个std::string):以及最后一段:“如果联合已经拥有一个字符串......”但是在如果union 没有string,那么我们为什么还要费心使用这样的placement new?只要赋值运算符调用析构函数从而销毁对象然后释放与构造函数相反的内存?
我的意思是他为什么不直接写这个?
sval = s;
我认为没有必要使用 sval.~string() 显式调用 dtor,因为赋值运算符会这样做。我的意思是不需要条件。
【问题讨论】:
-
想象一下当联合持有其他东西时调用字符串析构函数,这可能在指向字符串字节的堆分配部分的字节中。
-
记住
sval = s和sval.operator=(s)是一样的,因为sval还没有被构造,这不会起作用,因为同样的原因你不能调用任何成员函数在sval. -
@John Kugelman:如何像编辑过的那样突出显示文本引用中的代码?
-
“高亮代码”是什么意思?您是在问我如何将代码放入块引用中?我通过在代码前面加上
>加上五个空格来做到这一点。"> "用于块引用," "用于代码格式化。 -
@JohnKugelman:我的意思是,就像你在编辑我的代码时所做的那样。例如,如果我想从包含我的主题中的代码的书中复制文本。我从 ">" 行开始,但不能显示像你这样的代码。