为什么我所有的“this”指针都是相同的值?

Set*_*etu 41 c++ pointers this

我正在研究以下代码片段:

#include <iostream>                                                                                                                                                                                            
#include <vector>

class myclass
{
    public:
        myclass()
        {
            std::cout << this << std::endl;
        }
};

int main()
{
    std::vector<myclass> v;
    for(uint32_t i = 0; i < 10; i++)
        v.push_back(myclass());
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我正在使用 编译代码g++ main.cpp。当我执行编译的二进制文件时,我得到:

0x7ffebb8f8cab
0x7ffebb8f8cab
0x7ffebb8f8cab
0x7ffebb8f8cab
0x7ffebb8f8cab
0x7ffebb8f8cab
0x7ffebb8f8cab
0x7ffebb8f8cab
0x7ffebb8f8cab
0x7ffebb8f8cab
Run Code Online (Sandbox Code Playgroud)

我的问题是为什么所有的this指针都是相同的?如果我创建同一类的 10 个不同对象,则应该有 10 个不同的this指针。正确的?

据我了解,我的代码当前正在使用相同对象的引用来填充向量v。然而,我想要的是 10 个不同的对象myclass。我怎样才能得到这个?此代码是一个较大项目的一部分,该项目在new和方面存在一些问题delete。所以我无法使用该 API。我做错了什么以及如何解决这个问题?

Chr*_*ris 52

正如注释中所述,您将看到this临时指针,然后将临时指针复制到向量中。由于临时是临时的,您的系统在每次循环迭代中重用相同的内存位置。

但是,如果打印this向量元素的指针,您会注意到它们被放置在连续的内存位置,就像您对向量所期望的那样。

#include <iostream>                                                                                                                                                                                            
#include <vector>

struct myclass {
    myclass() {
        std::cout << this << std::endl;
    }

    void print() const { 
        std::cout << this << std::endl; 
    }
};

int main() {
    std::vector<myclass> v;
    for (uint32_t i = 0; i < 10; i++)
        v.push_back(myclass());

    std::cout << "\n";

    for (auto &x : v) 
        x.print();
        
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

#include <iostream>                                                                                                                                                                                            
#include <vector>

struct myclass {
    myclass() {
        std::cout << this << std::endl;
    }

    void print() const { 
        std::cout << this << std::endl; 
    }
};

int main() {
    std::vector<myclass> v;
    for (uint32_t i = 0; i < 10; i++)
        v.push_back(myclass());

    std::cout << "\n";

    for (auto &x : v) 
        x.print();
        
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

emplace_back成员函数std::vector可用于在向量中就地构造对象。

下面我们构造 5 个临时变量,我们可以看到它们重用了相同的内存,然后我们将push_back它们放置在向量中。

然后我们emplace_back直接在向量中构造五个对象。

可以看到,对象创建时打印的内存地址和后面打印的内存地址是一样的。

注意:如果大小超过容量,向量可能会重新分配和移动其内容。为了避免这种情况,我为向量保留了 10 的容量v

#include <iostream>                                                                                                                                                                                            
#include <vector>

struct myclass {
    myclass() {
        std::cout << this << std::endl;
    }

    void print() const { 
        std::cout << this << std::endl; 
    }
};

int main() {
    std::vector<myclass> v;

    v.reserve(10);

    for (uint32_t i = 0; i < 5; i++)
        v.push_back(myclass());

    for (uint32_t i = 0; i < 5; i++) 
        v.emplace_back();

    std::cout << "\n";

    for (auto &x : v) 
        x.print();
        
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

0x7ff7bfe6f5a8
0x7ff7bfe6f5a8
0x7ff7bfe6f5a8
0x7ff7bfe6f5a8
0x7ff7bfe6f5a8
0x7ff7bfe6f5a8
0x7ff7bfe6f5a8
0x7ff7bfe6f5a8
0x7ff7bfe6f5a8
0x7ff7bfe6f5a8

0x600001f4c030
0x600001f4c031
0x600001f4c032
0x600001f4c033
0x600001f4c034
0x600001f4c035
0x600001f4c036
0x600001f4c037
0x600001f4c038
0x600001f4c039
Run Code Online (Sandbox Code Playgroud)

  • 同样有趣的是,即使类没有声明任何存储,每个对象在内存中都是 1 个字节,并且“sizeof(myclass)”报告“1”。这可能看起来很奇怪,但是当您考虑到“this”指针对于每个创建的对象必须具有唯一的值时,这是完全有道理的:满足此规则的最简单方法是确保您永远不会拥有零字节的对象长度。 (13认同)
  • `0x7ff...` 位于 x86-64 上 48 位虚拟地址空间低半部分的顶部附近;正如代码所预期的那样,这是仅存在于自动存储中的临时堆栈地址(如命名局部变量)。`0x60000...` 很远,不是堆栈地址。这是有道理的,因为“std::vector”使用动态存储(通过“new”)为对象腾出空间。 (12认同)
  • 也许值得一提的是 [`std::vector::emplace_back()`](https://en.cppreference.com/w/cpp/container/vector/emplace_back) 它实际上确实在向量中就地构造了对象。 (6认同)