【发布时间】:2020-07-19 00:31:24
【问题描述】:
在尝试实现双指针的迭代器时,我发现了一些有趣的事情:
- 调用析构函数的时间让我感到困惑。
- 无法理解对象的内存地址。
说明
class A
我有一个名为A 的类,它将为整数序列(_ori_aa)分配一些内存。
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 对象中迭代双指针_aa。
为了观察,我在构造函数和析构函数中打印了自身的内存地址(this)。
函数meow()也打印内存地址,但用于手动调用。
template <typename T>
struct Iter {
Iter() { cout << '+' << this << endl; }
~Iter() { cout << '-' << this << endl; }
// ...
void meow() {
cout << '?' << this << endl;
}
// ...
};
main()
在主函数中,
- 我创建了一个
A的对象,然后调用aa(),这将生成一个Iter对象并按值返回。 - 我创建了一个
Iter的对象并手动调用meow()以查看其地址。 - 我使用基于范围的 for 循环来打印所有数字。
- 我打印一个分隔线来表示循环结束。
int main() {
A a;
Iter<int> aa = a.aa(); // copy by value
aa.meow();
for(const int & n : aa) {
cout << n << endl;
}
cout << "-------" << endl;
}
问题
这是程序的输出:
+0x7ffee567a9b0
?0x7ffee567a9b0
0
1
2
3
-0x7ffee567a988
-------
-0x7ffee567a9b0
我的问题是:
- 这些打印出来的地址对应什么操作?
- 我知道在
aa()中创建_iter时会打印第一个地址,但是什么时候调用析构函数呢?我以为_iter将在aa()返回后立即被销毁,而它似乎没有这样做。 - 我认为当对象
aa(main()中的局部变量)被销毁时会打印最后一个地址。既然和_iter的地址一样,那是不是说明_iter的内存已经被释放了呢?那为什么没有调用析构函数呢? - 第三个地址是什么?为什么它与构造函数打印的所有地址不同?为什么在 for 循环结束时调用析构函数?
环境
- 操作系统:macOS Catalina
- Apple 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;
}
感谢您的阅读!
【问题讨论】:
-
0x7ffee567a9b0 显然是aa的地址。 0x7ffee567a988 必须是 aa 的副本(您没有实现复制构造函数,因此您看不到它正在构造。
-
将复制构造函数添加到
Iter,您应该会看到更多内容。 -
并使用 -fno-elide-consteructors 进行编译,以确保您看到复制构造函数正常工作。
-
看这个:onlinegdb.com/r18W8QYwL 第一个构造函数就是你创建它的时候。第二个副本由 for 循环制作。 for 循环也会破坏副本。然后在 main() 结束时,你所做的那个被破坏了。
标签: c++ for-loop iterator c++17 destructor