有人可以解释这个C ++联合示例吗? [英] Could someone explain this C++ union example?

查看:72
本文介绍了有人可以解释这个C ++联合示例吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在cppreference.com上找到了此代码。这是我见过的最奇怪的C ++,对此我有一些疑问:

I found this code on cppreference.com. It's the strangest C++ I've seen, and I have a few questions about it:

union S
{
    std::string str;
    std::vector<int> vec;
    ~S() {}  
};          

int main()
{
    S s = { "Hello, world" };
    // at this point, reading from s.vec is undefined behavior
    std::cout << "s.str = " << s.str << '\n';
    s.str.~basic_string<char>();
    new (&s.vec) std::vector<int>;
    // now, s.vec is the active member of the union
    s.vec.push_back(10);
    std::cout << s.vec.size() << '\n';
    s.vec.~vector<int>();
}

我想确保我做对了一些事情。

I want to make sure I've got a few things right.


  1. 工会通过删除默认构造函数来强迫您初始化一个工会成员,在这种情况下,他使用Hello World初始化了字符串。

  2. 初始化字符串后,从技术上讲向量还不存在吗?我可以访问它,但是还没有构造它?

  3. 他通过调用其析构函数显式销毁了字符串对象。在这种情况下,当S超出范围时,将调用〜S()析构函数吗?如果是这样,在哪个对象上?如果他没有在字符串上显式调用析构函数,那是内存泄漏吗?我倾向于不这样做,因为琴弦会自行清理,但对于工会我是未知的。他自己调用了字符串和向量的析构函数,因此〜S()析构函数似乎没用,但是当我删除它时,编译器将不允许我对其进行编译。

  4. 这是我第一次看到有人使用new运算符将对象放置在堆栈上。在这种情况下,这是现在可以使用向量的唯一方法吗?

  5. 当您像使用向量一样使用new放置时,不应该在其上调用delete,因为尚未分配新的内存。通常,如果将new放置在堆上,则必须释放内存以免泄漏,但是在这种情况下,如果他让vector和union超出范围而不调用析构函数,会发生什么情况?

  1. The union forces you to initialise one of the union members by deleting the default constructors, in this case he initialised the string with Hello World.
  2. After he's initialised the string, the vector technically doesn't exist yet? I can access it, but it isn't constructed yet?
  3. He explicitly destroys the string object by calling its destructor. In this case when S goes out of scope, will the ~S() destructor be called? If so, on which object? If he doesn't call the destructor explicitly on the string is it a memory leak? I'm leaning towards no because strings clean themselves up, but for unions I don't know. He calls the destructor for both the string and vector himself, so the ~S() destructor seems useless, but when I delete it my compiler won't let me compile it.
  4. This is the first time I've seen someone use the new operator to place an object on the stack. In this case is this the only way now that the vector can be used?
  5. When you use placement new as he does with the vector, you're not supposed to call delete on it because new memory hasn't been allocated. Usually if you placement new on the heap you have to free() the memory to avoid a leak, but in this case what happens if he let's the vector and union go out of scope without calling the destructor?

我觉得这很令人困惑。

推荐答案


  1. 是的。

  2. 因为向量和字符串使用相同的底层存储(这是 union 的工作方式),并且该存储区当前包含一个字符串,因此没有Verver身份,尝试访问该字符串将是不确定的。不是说它还没有被建造;

  3. 每当 S 熄灭时,它就会被构造 。在范围上,它的析构函数被调用。在这种情况下,这就是 union 的析构函数,该析构函数被明确定义为不执行任何操作(因为联合会不知道哪个成员处于活动状态,因此它实际上无法执行应有的操作) 。因为联合体无法知道哪个成员处于活动状态,所以如果您不显式调用字符串的析构函数,则它将无法知道那里有一个字符串,并且该字符串也不会被清除。当存在具有非平凡析构函数的并集成员时,编译器使您可以编写自己的析构函数,因为它不知道如何清理并希望您这样做。在此示例中,您也不知道如何清除它,因此您在联合的析构函数中不执行任何操作,并使使用 S 的人在正确的元素上调用析构函数

  4. 这称为新放置,是在现有内存位置中构造对象而不是分配新对象的典型方法。除联合以外,它还有其他用途,但我相信这是不使用未定义行为就可以将向量引入此联合的唯一方法。

  5. 如第3部分所述,当 s 超出范围,它不知道它是否包含字符串或向量。 〜S 析构函数不执行任何操作,因此您需要使用其自己的析构函数销毁向量,例如使用字符串。

  1. Yes, exactly.
  2. Because the vector and the string use the same underlying storage (which is how unions work), and that storage currently contains a string, there is no place for a vertor to be and trying to access it would be undefined. It’s not that it hasn’t been constructed yet; it’s that it cannot be constructed because there’s a string in the way.
  3. Whenever an S goes out of scope, its destructor is called. In this case, that’s the union’s destructor, which was explicitly defined to do nothing (because the union can’t know which member is active, so it can’t actually do what it’s supposed to). Because the union cannot know which of its members is active, if you don’t explicitly call the destructor of the string, it cannot know there was a string there and the string will not be cleaned up. The compiler makes you write your own destructor when there are union members with non-trivial destructors, because it can’t know how to clean that up and hopes that you do; in this example you don’t know how to clean it up either, so you do nothing in the union’s destructor and make the person who uses S call the destructor on the correct element manually.
  4. This is called "placement new", and is the typical way to construct an object in an existing memory location instead of allocating a new one. There are uses for it besides unions, but I believe that it’s the only way to get a vector into this union without using undefined behavior.
  5. As addressed in part 3), when s goes out of scope, it doesn’t know if it holds a string or a vector. The ~S destructor does nothing, so you need to destroy the vector with its own destructor, like with the string.

要了解为什么工会不能自动知道要调用哪个析构函数,请考虑以下替代函数:

To see why the union can’t automatically know which destructor to call, consider this alternate function:

int maybe_string() {
    S s = {"Hello, world"};
    bool b;
    std::cin >> b;
    if (b) {
        s.str.~basic_string<char>();
        new (&s.vec) std::vector<int>;
    }
    b = false;
    // Now there is no more information in the program for what destructor to call.
}

在函数末尾,编译器无法知道 s 包含字符串或向量。如果您不手动调用析构函数(假设您有一种方法可以告诉您,我认为您在这里不知道这样做),那么它将必须谨慎行事并且不得销毁任何一个成员。 C ++的创建者并没有制定复杂的规则来确定编译器何时可以销毁活动成员,什么时候不销毁任何东西,而是决定保持简单,并且永远不要自动销毁联合的活动成员,而是强制执行该活动。程序员手动完成。

At the end of the function, the compiler has no way to know if s contains a string or a vector. If you don’t call a destructor manually (assuming you had a way to tell, which I don’t think you do here), it will have to play it safe and not destroy either member. Instead of having complicated rules about when the compiler would be able to destroy the active member and when it wouldn’t destroy anything, the creators of C++ decided to keep things simple and just never destroy the active member of a union automatically and instead force the programmer to do it manually.

这篇关于有人可以解释这个C ++联合示例吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆