C++的拷贝和引用

8
学习智能指针时发现的一个问题,以前一直没和内存连接起来理解。。。

从一段程序引入

auto service1 = std::make_shared<area_detact_service>();  
  
auto device1 = std::make_unique<device>();  
  
device1->add_service(service1);  
  
  
auto ptr1 = service1;  
  
const auto& ptr2 = service1;

在学习智能指针的时候,我发现,使用const auto& ptr2 = service1传入的函数,并没有增加service1引用计数,当时我一直不懂为什么?

在进一步学习过后,我结合AI和资料,终于明白了其原因。

拷贝在内存中真正发生的事情

        变量名           内存地址        值
    ┌─────────────┐   ┌─────────────┐ ┌─────┐
    │  service1   │──→│  0x1000     │→│     │
    └─────────────┘   └─────────────┘ └─────┘
    
    ┌─────────────┐   ┌─────────────┐ ┌─────┐
    │    ptr1     │──→│  0x1020     │→│     │ ← 独立副本
    └─────────────┘   └─────────────┘ └─────┘

auto ptr1 = service1 实际上是开辟了一个新的内存空间, 然后将ptr1指向了新的地址

引用在内存中真正发生的事情

┌─────────────────────┐
│    符号表/编译器    │
├─────────────────────┤
│ service1   → 0x1000 │
│ ptr2       → 0x1000 │ ← 指向同一地址
└─────────────────────┘
            │
            ▼
┌─────────────┐ ┌─────┐
│  0x1000     │→│     │ ← 实际的内存位置
└─────────────┘ └─────┘

实际上,const auto& ptr2 = service1并不会产生任何开销,通过编译器的操作,让引用看起来实际就是原指针的“别名”!

实际上,访问ptr的地址,它的地址也和原地址一样!

代码如下所示:

int main(int argc, char *argv[]) {  
    // 设置控制台为UTF-8编码  
    SetConsoleOutputCP(CP_UTF8);  
  
    auto service1 = std::make_shared<area_detact_service>();  
  
    auto device1 = std::make_unique<device>();  
  
    device1->add_service(service1);  
  
  
    auto ptr1 = service1;  
  
    const auto &ptr2 = service1;  
  
    std::cout << &service1 << std::endl;  
    std::cout << &ptr1 << std::endl;  
    std::cout << &ptr2 << std::endl;  
  
  
    return 0;  
}

打印结果如下所示:

服务资源已新建
实时检测服务已新建
设备已创建
0x94709ffc80
0x94709ffc60
0x94709ffc80
实时检测服务已释放
服务资源已释放

可以看到,即使访问指针的地址,ptr2的地址也和service1地址一样,而ptr1则明显不同。

回到开头的问题:

auto ptr1 = service1;  
  
const auto& ptr2 = service1;

为什么第二个指针没有增加引用计数呢?是因为没有一个新的指针指向其资源,所以它只是单纯的给service1 起了一个“外号”!

  题外话:拷贝和引用的关系,很像硬连接和软链接的关系。