关于c ++:为什么我的对象看起来不在使用`new`?

Why does my object appear to be on the heap without using `new`?

我开始学习动态内存分配的主题。

我有以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include"A.h"
#include"B.h"

using namespace std;

int main() {

   /* Both objects on Stack */

   A classAStack;
   B classBStack;

   /* Both objects on Heap*/
   //  A *classAHeap = new A();
   //  B *classBHeap = new B();

   /* A objects on Heap B ???*/
   A *classAHeap = new A();

   return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef A_H_
#define A_H_

#include <iostream>
#include"B.h"

class A {
public:
   A();
   virtual ~A();

public:
   B b;
};

#endif /* A_H_ */
1
2
3
4
5
6
7
8
#include"A.h"

A::A() {
   std::cout <<"Constructor A called" << std::endl;
}

A::~A() {
}
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef B_H_  
#define B_H_

#include <iostream>

class B {
public:
  B();
  virtual ~B();
};

#endif /* B_H_ */
1
2
3
4
5
6
7
8
#include"B.h"

B::B() {
  std::cout <<"Constructor B called" << std::endl;
}

B::~B() {
}

调试器的输出是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Temporary breakpoint 6, main () at ../src/HeapStackTest02.cpp:18
18    A classAStack;

Breakpoint 4, B::B (this=0x23aa58) at ../src/B.cpp:12
12    std::cout <<"Constructor B called" << std::endl;

Breakpoint 5, A::A (this=0x23aa50) at ../src/A.cpp:13
13    std::cout <<"Constructor A called" << std::endl;

Breakpoint 4, B::B (this=0x23aa40) at ../src/B.cpp:12
12    std::cout <<"Constructor B called" << std::endl;

Breakpoint 4, B::B (this=0x60004b048) at ../src/B.cpp:12
12    std::cout <<"Constructor B called" << std::endl;

Breakpoint 5, A::A (this=0x60004b040) at ../src/A.cpp:13
13    std::cout <<"Constructor A called" << std::endl;

Breakpoint 1, main () at ../src/HeapStackTest02.cpp:30
30    return 0;

我的问题是:

A类的成员变量b在哪里?

如果我查看0x23A节中的地址,它似乎是堆栈,而0x6000节似乎是堆。

我正在使用Windows 64位系统。

为什么成员变量b也在堆中,而没有调用new运算符?


成员是b是动态分配的对象的一部分,因此它是动态分配的一部分,并且在内存中处于同一位置。

如果成员不是对象的一部分,还剩下什么?您将动态分配什么?

这就是当您看到new时,应该避免使用术语"堆上"。你所说的"堆"不仅仅是你所说的。不,new动态分配对象和对象直接包含的所有内容。你如何写一个对象的声明和它是"堆上"还是"堆上"之间的任何心理联系都注定要失败。

确定对象的存储持续时间的唯一可靠方法是了解其历史记录;确定对象存储位置的唯一可靠方法是不用麻烦,因为您不需要这样做。


要回答这个问题,我们首先要了解我们有多少B类型的实例。答案是3。

一个实例是类型A(堆栈上)的成员B。另一个是堆栈上B的实例,第三个是堆上A类型的成员B的实例。

为什么要堆起来?它就在那里,因为您在堆上创建了类型A的实例,而A的实例将B的实例作为成员。

所以B的3个实例中的一个在堆上,另2个在堆栈上。


考虑一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <string>

class B {
public:
    int64_t x = 42;
    int64_t y = 7;
};

class A1 {
public:
    B b;
};

class A2 {
public:
    A2() { b = new B(); }
    B* b;
    ~A2() { delete b; }
};

int main() {
    std::cout << sizeof(A1) << std::endl;   // prints 16
    std::cout << sizeof(A2) << std::endl;   // prints 8

    // Section (2)
    A1 a1;  // Instance of A1 and B are on the stack
    A2 a2;  // Instance of A2 is on the stack. B is on the heap.

    A1* a3 = new A1();
    std::cout << a3 == &(a3->b) << std:: endl;  // prints true

    A2* a4 = new A2();
    std::cout << a4 == a4->b << std::endl;  // prints false

    return 0;
}