C++的拷贝和引用
学习智能指针时发现的一个问题,以前一直没和内存连接起来理解。。。
从一段程序引入
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 起了一个“外号”!
题外话:拷贝和引用的关系,很像硬连接和软链接的关系。