簡體   English   中英

c++ 基於范圍的for循環會調用迭代器的析構函數嗎?

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

在嘗試實現雙指針的迭代器時,我發現了一些有趣的東西:

  • 調用析構函數的時間讓我感到困惑。
  • 無法理解對象的 memory 地址。

解釋

class A

我有一個名為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

還有一個名為Iter的結構,它可以幫助我在A object 中迭代雙指針_aa

為了觀察,我在構造函數和析構函數中打印了自身( this )的 memory 地址。

function meow()也打印 memory 地址,但它用於手動調用。

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

main()

在主function中,

  1. 我創建了一個A的 object ,然后調用aa() ,這將生成一個Iter object並按值返回。
  2. 我創建了一個Iter的 object 並手動調用meow()查看其地址
  3. 我使用基於范圍的 for 循環來打印所有數字。
  4. 我打印一個分隔符來指示循環的結束
int main() {
    A a;
    Iter<int> aa = a.aa();      // copy by value
    aa.meow();
    for(const int & n : aa) {
        cout << n << endl;
    }
    cout << "-------" << endl;
}

問題

這是程序的output:

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

我的問題是:

  1. 這些打印出來的地址分別對應什么操作?
  2. 我知道在aa()中創建_iter時會打印第一個地址,但是什么時候調用析構函數? 我認為_iter將在aa()返回后被銷毀,而它似乎沒有這樣做。
  3. 我認為當 object aamain()中的局部變量)被破壞時會打印最后一個地址。 既然和_iter的地址一樣,那是不是意味着 _iter 的_iter已經被釋放了呢? 那么為什么沒有調用析構函數呢?
  4. 第三個地址是什么? 為什么它與構造函數打印的所有地址不同? 為什么在 for 循環結束時調用析構函數?

環境

  • 操作系統:macOS Catalina
  • 蘋果 clang 版本 11.0.3 (clang-1103.0.32.29)
  • 編譯選項: -std=c++17

代碼

以下是完整的代碼,

#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;
}

感謝您的閱讀!

基於范圍的 for 語句:

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

只是以下的語法糖(參見cppref ):

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

這應該可以幫助您了解其他Iter的構建位置和銷毀位置。

請注意,該程序中恰好構造了兩個Iter對象:一個名為aa ,另一個在脫糖 for 語句中名為__begin aa()中名為_iter的那個是在aa中構建的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM