简体   繁体   English

c++ 基于范围的for循环会调用迭代器的析构函数吗?

[英]Will c++ range-based for loop call the destructor of iterator?

When trying to implement an iterator of a double-pointer, I found something interesting:在尝试实现双指针的迭代器时,我发现了一些有趣的东西:

  • The timing of the destructor being called confuses me.调用析构函数的时间让我感到困惑。
  • Unable to understand the memory addresses of the objects.无法理解对象的 memory 地址。

Explanation解释

class A

I have a class called A , which will allocate some memory for a sequence of integers ( _ori_aa ).我有一个名为A的 class ,它将为整数序列( _ori_aa )分配一些 memory 。

class A {
public:
    // constructor and destructor
    // ...
    Iter<int> aa() const {
        Iter<int> _iter;
        _iter.set(_aa, _len);
        return _iter;
    }
private:
    const int _len;
    int * _ori_aa;  // sequence of numbers: {0, 1, 2, 3}
    int ** _aa;     // pointers to _ori_aa: {_ori_aa, _ori_aa+1, ...}
};

struct Iter

And a struct called Iter , which can help me to iterate the double-pointer _aa in an A object.还有一个名为Iter的结构,它可以帮助我在A object 中迭代双指针_aa

For observation, I print the memory address of itself ( this ) in the constructor and destructor.为了观察,我在构造函数和析构函数中打印了自身( this )的 memory 地址。

The function meow() also prints the memory address, but it is used for a manual call. function meow()也打印 memory 地址,但它用于手动调用。

template <typename T>
struct Iter {
    Iter() { cout << '+' << this << endl; }
    ~Iter() { cout << '-' << this << endl; }
    // ...
    void meow() {
        cout << '?' << this << endl;
    }
    // ...
};

main()

In the main function,在主function中,

  1. I create an object of A and then call aa() , which will generate an Iter object and return by value.我创建了一个A的 object ,然后调用aa() ,这将生成一个Iter object并按值返回。
  2. I create an object of Iter and call meow() manually to see its address .我创建了一个Iter的 object 并手动调用meow()查看其地址
  3. I use range-based for loop to print all the numbers.我使用基于范围的 for 循环来打印所有数字。
  4. I print a divider to indicate the end of the loop .我打印一个分隔符来指示循环的结束
int main() {
    A a;
    Iter<int> aa = a.aa();      // copy by value
    aa.meow();
    for(const int & n : aa) {
        cout << n << endl;
    }
    cout << "-------" << endl;
}

Problem问题

This is the output of the program:这是程序的output:

+0x7ffee567a9b0
?0x7ffee567a9b0
0
1
2
3
-0x7ffee567a988
-------
-0x7ffee567a9b0

My questions are:我的问题是:

  1. What operations do these printed addresses correspond to?这些打印出来的地址分别对应什么操作?
  2. I know the first address is printed when _iter is created in aa() , but when does the destructor be called?我知道在aa()中创建_iter时会打印第一个地址,但是什么时候调用析构函数? I thought _iter will be destroyed just after aa() return, while it didn't seem to do.我认为_iter将在aa()返回后被销毁,而它似乎没有这样做。
  3. I thought the last address is printed when the object aa ( local variable in main() ) is destroyed.我认为当 object aamain()中的局部变量)被破坏时会打印最后一个地址。 Since it is the same as the address of _iter , does it mean the memory of _iter had already been freed?既然和_iter的地址一样,那是不是意味着 _iter 的_iter已经被释放了呢? Then why the destructor didn't be called?那么为什么没有调用析构函数呢?
  4. What is the third address?第三个地址是什么? Why it is different from all the addresses printed by the constructor?为什么它与构造函数打印的所有地址不同? Why a destructor is called at the end of the for loop?为什么在 for 循环结束时调用析构函数?

Environment环境

  • OS: macOS Catalina操作系统:macOS Catalina
  • Apple clang version 11.0.3 (clang-1103.0.32.29)苹果 clang 版本 11.0.3 (clang-1103.0.32.29)
  • Compilation options: -std=c++17编译选项: -std=c++17

Code代码

The following is the complete code,以下是完整的代码,

#include <iostream>

using namespace std;

template <typename T>
struct Iter {
    Iter() { cout << '+' << this << endl; }
    ~Iter() { cout << '-' << this << endl; }

    T ** pp {nullptr};
    int len {0};
    int it {0};

    void meow() {
        cout << '?' << this << endl;
    }
    void set(T ** pi, int l) {
        pp = pi;
        len = l;
    }
    Iter & begin() {
        it = 0;
        return *this;
    }
    int end() const {
        return len;
    }

    T & operator*() {
        return *pp[it];
    }
    bool operator!=(int rhs) {
        return this->it < rhs;
    }
    Iter & operator++() {
        ++it;
        return *this;
    }
};

class A {
public:
    A() : _len(4) {
        _ori_aa = new int [_len];
        _aa = new int * [_len];
        for(int i = 0; i < _len; i++) {
            _ori_aa[i] = i;
            _aa[i] = _ori_aa + i;
        }
    }
    ~A() {
        delete [] _aa;
        delete [] _ori_aa;
    }
    Iter<int> aa() const {
        Iter<int> _iter;
        _iter.set(_aa, _len);
        return _iter;
    }
private:
    const int _len;
    int * _ori_aa;
    int ** _aa;
};

int main() {
    A a;
    Iter<int> aa = a.aa();      // copy by value
    aa.meow();
    for(const int & n : aa) {
        cout << n << endl;
    }
    cout << "-------" << endl;
}

Thank you for your reading!感谢您的阅读!

A range-based for statement:基于范围的 for 语句:

for (const int & n : aa) {
    cout << n << endl;
}

Is just syntax sugar for the following (see cppref ):只是以下的语法糖(参见cppref ):

{
    auto&& __range = aa;
    auto __begin = __range.begin();
    auto __end = __range.end();
    for (; __begin != __end; ++__begin) {
        const int & n = *__begin;
        cout << n << endl;
    }
}

Which should help you understand both where your other Iter is being constructed and where it is being destroyed.这应该可以帮助您了解其他Iter的构建位置和销毁位置。

Note that there are exactly two Iter objects being constructed in this program: the one named aa and the one in the desugared for statement named __begin .请注意,该程序中恰好构造了两个Iter对象:一个名为aa ,另一个在脱糖 for 语句中名为__begin The one named _iter inside of aa() is constructed in place in aa . aa()中名为_iter的那个是在aa中构建的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM