【问题标题】:Member variables clearing while using destructor使用析构函数时清除成员变量
【发布时间】:2015-08-04 22:20:33
【问题描述】:

我正在通过一些关于 C++ 的在线测验,下面是我遇到的问题

http://www.interqiew.com/ask?ta=tqcpp01&qn=3

class A
{
public:
    A(int n = 2) : m_i(n) { }

    ~A() { std::cout << m_i; }

protected:
    int m_i;
};

class B
    : public A
{
public:
    B(int n) : m_a1(m_i + 1), m_a2(n) { }

public:
    ~B()
    {
        std::cout << m_i;
        --m_i;
    }

private:
    A m_a1;
    A m_a2;
};

int main()
{
    { B b(5); }

    std::cout << std::endl;

    return 0;
}

答案是“2531” -

我期待答案是“21” - 理由如下(这似乎是错误的):

对象 B 由三个成员变量创建,起始值为 2 - 253

所以当调用析构函数时会以相反的顺序被删除。

对于这种情况,析构函数将调用继承的部分 - 我们打印 2 我们递减值并转到 1,然后打印删除时的基数 - 所以回答 21

如何打印变量 m_a2 和 m_a1 - 无法理解。 它也在基础部分(即 A 类)中打印(值 53)

【问题讨论】:

  • B 中无用的代码混淆将 A 的成员初始化为自己的成员。
  • m_a2m_a1 在它们的包含对象 b 被销毁时被销毁。由于A的析构函数输出了东西,所以东西被销毁的时候就输出了。

标签: c++ inheritance destructor


【解决方案1】:

让我们考虑构造函数

B(int n) : m_a1(m_i + 1), m_a2(n) { }

相当于

B(int n) : A(), m_a1(m_i + 1), m_a2(n) { }

所以首先 m_i 由构造函数 A 的默认参数初始化,并且等于 2。 那么 m_a1 会被 m_i + 1 初始化,即等于 3。最后 m_a2 会等于 5 来调用 B( 5 )

然后当 B 的析构函数被调用时输出

std::cout << m_i;

那是

2

然后减小m_i

--m_i;

数据成员的析构函数的调用顺序与它们的构造相反。所以首先会调用 m_a2 的析构函数,它会输出

5

然后会调用m_a1的析构函数并输出

3

最后会调用输出基类的析构函数

1

所以你会得到

2531

至于你的问题,A 的析构函数被调用了 3 次:B 类的数据成员 m_a1 和 m_a2 被销毁(因为它们的类型为 A 类)和基类构造函数被调用时两次。

【讨论】:

  • 谢谢弗拉德 - 我能够跟随这部分直到 m_a2 的析构函数被调用 - 混淆部分是“并且它将输出” - 我能够理解部分 5 和 3 将被破坏 -我的理解是当我们离开继承的类 B 时会发生这种情况 - 但输出显示的是这些值是在基类中打印的 - m_a2 和 m_a1 的值如何在基类中可用以及它们是如何打印的 - 如果你可以再给一个努力解释那部分-我会很高兴-
  • 我认为根据您编辑的答案-每次销毁数据成员基类都会被调用,因为它们属于 A 类-是我错过的部分-非常感谢您的解释-我明白了现在就来!!
【解决方案2】:

2531 是正确答案。调用析构函数的顺序总是与调用构造函数的顺序相反。

  • 2 - B 的析构函数
  • 5,3 - 与初始化顺序相反的类字段的析构函数
  • 1 - 超类的析构函数

见:Order of member constructor and destructor calls

【讨论】:

  • 5,3 - 与初始化顺序相反的类字段的析构函数——从概念上讲,我确实理解它将以相反的顺序完成。我仍然感到困惑的是下面的代码行 ~A() { std::cout
  • @oneday B 的两个A 成员变量与B 对象不共享相同的m_i 值。一个B 对象有:一个A 基础对象和两个A 成员对象。基础对象并不真正与两个成员对象交互,除非作为构造函数中的参数。
  • @oneday "... and remove 53" C++ 永远不会只删除一个对象。它总是先调用析构函数。
  • 嗨 @jaggedSpire 和 NO_NAME - 感谢您的回答 - 我认为我缺少的部分是 B 类的成员变量属于 A 类 - 所以当它们被删除时,我们将调用基类- 所以打印输出 - 感谢您的回答并帮助我理解
【解决方案3】:

我相信成员对象的析构函数都是在指定的析构函数之后调用的,与它们在类中的声明顺序相反,然后调用基对象的析构函数。

所以构造的顺序是这样的:

  1. 构造基类A,默认值为m_i 2
  2. 构造B成员m_a1,其值基于初始化m_i
  3. 使用来自参数 5 的值构造 B 成员 m_a2

和销毁如下:

  1. B 执行析构函数体
  2. 破坏m_a2
  3. 破坏m_a1
  4. 销毁基类A

翻译为:

  1. 打印m_i
  2. 递减m_i
  3. 打印m_a2
  4. 打印m_a1
  5. 打印m_i

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-15
    • 1970-01-01
    • 2018-03-18
    • 2017-10-03
    • 2011-11-06
    • 2014-05-22
    • 2016-01-29
    • 1970-01-01
    相关资源
    最近更新 更多