本篇摘要

  • 本篇将以最通俗易懂的语言,形象的讲述为什么很多情境下,我们优先考虑的使用指针而不是对象本身,本篇将给出你答案!

在这里插入图片描述

一.从一个生活例子说起,形象秒懂

想象一下,你去图书馆借书,下面你有两种选择:

  • 把整本书复印一份带回家,但是,书很厚,复印要时间,占地方,还容易丢。

  • 只拿一张“借书卡”,上面写着书名和位置,而且, 轻便、快速、随时可以查。

此时我们大多数人就会直接选择第二种方案了,主打一个通透!

在编程中:

“复印书” = 直接定义对象

Book book;  // 在函数里定义,函数结束就没了

“借书卡” = 指针

Book* ptr = new Book();  // 拿个“卡”,书在别处(堆上)

指针就像“借书卡”——它不存对象本身,只存对象的“地址”。

二·那为啥不直接“看书”,非要用“借书卡”呢(也就是为什么选择用指针而不是对象呢)?

可以这么认为因为有时候,“直接看书”根本做不到!

下面我们经常下面几个方面展开叙述:

原因简要说明
动态生命周期管理对象可以在运行时创建/销毁,不受作用域限制
多态基类指针可以指向派生类对象
避免大对象拷贝指针传递比对象拷贝更高效
实现复杂数据结构如链表、树、图等需要指针连接节点
延迟初始化对象可以在需要时才创建

流程图效果:
在这里插入图片描述

动态生命周期管理

下面举个通俗易懂例子:

// 对象在栈上,函数结束就销毁
void badExample() {MyClass obj;// obj 在函数返回时自动析构
}// 指针可以控制对象生命周期
MyClass* ptr = new MyClass();
delete ptr; // 手动控制销毁
  • 可以看出这里如果使用指针的生命周期是由我们自己控制的,不受作用域限制!

  • 适用于:数据库连接、网络套接字、单例等需要跨函数/模块存在的对象。

多态

下面从我们最熟悉的继承多态来分析下:

  • 比如我们如果想写代码的时候,当描述的对象有些相似的特征,我们就会考虑到进行继承多态来简化操作,便于管理,因此这里的基类指针就是我们必不可少的了!
#include<iostream>
using namespace std;
class Animal {
public:virtual void speak() = 0;
};class Dog : public Animal { void speak() override { cout << "我是一只狗,我要叫了:"<<"汪\n"; } };
class Cat : public Animal { void speak() override { cout << "我是一只猫,我要叫了:"<<"喵\n"; } };int main(){
//  基类指针可以指向任意派生类
Animal* animals[2];
animals[0] = new Dog();
animals[1] = new Cat();for (int i = 0; i < 2; ++i) {animals[i]->speak(); 
}}

效果展示:

在这里插入图片描述

  • 看到这就是我们非常亲切的多态效果了!

  • 这是直接定义对象无法实现的!

避免大对象拷贝

这里,我们回忆下,通常比如函数传参的时候用的要么是对象,指针,引用,而这里我们重点看对象和指针的区别。

下面先看下例子:

class BigObject
{char data[1024 * 1024]; // 1MB
};//  每次传参都会拷贝 1MB 内存
void process1(BigObject obj)
{cout << "对象" << endl;
};//  指针只传 8 字节地址
void process(BigObject *ptr)
{cout << "指针" << endl;
};void process2(BigObject&a){
cout << "引用" << endl;}int main()
{BigObject *p = nullptr;int s1=clock();process(p); // 指针int e1=clock();BigObject b;int s2=clock();process1(b); // 对象int e2=clock();int s3=clock();process2(b);int e3=clock();cout<<"指针耗时:"<<e1-s1<<" 消耗内存: "<<sizeof( BigObject *)<<endl;cout<<"对象耗时:"<<e2-s2<<" 消耗内存: "<<sizeof( BigObject )<<endl;cout<<"引用耗时:"<<e3-s3<<endl;}

运行效果:

在这里插入图片描述

当然这里每次时间可能不同,这里忽视,我们可以看到对象耗时是最长的,其次是指针,而引用达到了最快。

下面解释下原因:

  • 对象:需要拷贝构造,消耗一个对象大小,故耗时耗内存。
  • 指针:需要构建指针,故消耗指针大小。
  • 引用:直接起别名,几乎不占内存,由编译器在编译期处理别名关系。

因此,这里如果条件符合,一定是选择指针比较优的(如果对象特别大,对象的话,这不就是自己给自己找麻烦)!

实现复杂数据结构

想做个“链表”或“树”,比如图论的一些算法等具有连接关系的模型,都是需要指针来解围的(这里顺便推荐下博主的图论专栏,讲的超级详细:图论专栏)。

比如你要做微信好友关系链:

struct Person {string name;Person* friend;  // 指向下一个好友
};

没有指针,你怎么表示“张三的好友是李四”?

这里我们经常设置进去的是指针来表示这种关系,似乎都已经成为常态了!

延迟初始化

#include <iostream>
#include <ctime>
using namespace std;
class Animal
{
public:virtual void speak() = 0;
};class Dog : public Animal
{void speak() override { cout << "我是一只狗,我要叫了:" << "汪\n"; }
};
class Cat : public Animal
{void speak() override { cout << "我是一只猫,我要叫了:" << "喵\n"; }
};class Player
{
public:Animal *Animaler = nullptr;void start(){Animaler = new Dog(); // 按需创建}
};
int main()
{Player p;//此时没有Animaler指向的对象,也就是没有对象产生p.start();//此时才构造处对象p.Animaler->speak();
}

效果展示:

在这里插入图片描述

  • 对象只在真正需要时才分配内存。,真正做到了方便,节省资源!

四·直接定义对象的优势

下面看下优势总结:

优点说明
自动管理内存RAII,作用域结束自动析构
性能更高无间接访问开销
更安全不会空指针、内存泄漏(如果不用 new)

比如:

void goodExample() {MyClass obj; obj.doWork();
} // 自动调用 ~MyClass()
  • 当我们使用对象的时候(非new),它会自己出了作用域自动析构,安全感拉满,但是new了就需要手动delete否则内存泄漏!

  • 能用栈对象就用栈对象!

五· 普通指针的坏处

指针有没有坏处?当然有!指针就像“双刃剑”:

  • 优点:灵活、高效、支持多态 。
  • 缺点:容易空指针、内存泄漏、野指针。

存在隐患:

非法访问:

Book* ptr = nullptr;
ptr->read();  //  空指针崩溃!段错误!

忘记手动释放:

Book* ptr = new Book();
// 忘了 delete ptr;  // 内存泄漏!

六· 现代C++怎么解决这些问题?——“智能指针”

别怕,C++11 以后有了“智能指针”,它像“自动还书机”:

#include <memory>
// 自动管理内存,不用手动 delete
unique_ptr<Book> ptr = make_unique<Book>();
ptr->read();  // 正常使用
// 函数结束,自动释放内存,安全又省心
  • 这就类似我们普通指针,赋能添加了自动delete工作!

  • 有了智能指针,从此麻麻再也不怕我用指针操作,忘记delete了!

七· 何时使用指针 vs 直接定义对象?

下面基于上文所有的举例以及博主总结得到下面的使用推荐方法:

场景推荐方式备注
小对象,函数内使用MyClass obj;栈分配,自动管理生命周期
需要多态(猫/狗)Base* ptr = new Derived();需配合delete手动释放
大对象,避免拷贝func(Object* ptr)指针传递避免拷贝开销
动态结构(链表)Node* next典型指针链接结构
现代C++项目unique_ptr/shared_ptr优先使用智能指针管理资源

八· 总结

用指针不是因为“高级”,而是因为“需要”,指针不是“炫技”,而是为了解决实际问题而存在的工具。

记住:

能不用指针就不用,要用就用智能指针。

如果你觉得这篇文章帮你理清了思路,欢迎点赞、收藏、转发!

欢迎在评论区留言:

“我以前一直搞不懂指针,现在终于明白了!”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/93841.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/93841.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/93841.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CAMx大气污染模拟全流程:Linux编译/多重嵌套配置/SMOKE清单预处理/SA-DDM-PA工具应用与科研绘图结果可视化分析

CAMx模型是一个基于大气化学&#xff0c;针对臭氧、颗粒物和雾霾天气过程的大气污染物计算模型。【目标】&#xff1a;1、掌握CAMx模式的区域空气质量模拟案例配置技术方法2、掌握SMOKE模型的CAMx模式大气排放清单输入准备方法3、掌握CAMx模式污染来源解析工具&#xff08;SA&a…

嵌入式学习笔记-MCU阶段-DAY10ESP8266模块

1.ESP8266概述 官方网址&#xff1a;ESP8266 Wi-Fi MCU I 乐鑫科技 (espressif.com.cn) ESP8266模块---wifi模块 产品特点&#xff1a; 2.ESP8266中的wifi: ESP8266EX ⽀持 TCP/IP 协议&#xff0c;完全遵循 802.11 b/g/n WLAN MAC 协议&#xff0c;⽀持分布式控制功能 (DC…

如何快速通过软件项目验收,第三方软件检测机构的重要性

在客户和开发团队之间&#xff0c;最后临门一脚的项目验收环节总容易出现各种问题&#xff0c;以至于时间无限拉长&#xff0c;久久不见结束&#xff0c;为此给大家准备了一份如何快速通过软件项目验收的内容来帮助大家结束持久战。 一、项目验收准备材料 &#xff08;一&…

洛谷做题3:P5711 【深基3.例3】闰年判断

文章目录题目描述输入格式输出格式输入输出样例分析代码题目描述 输入一个年份&#xff0c;判断这一年是否是闰年&#xff0c;如果是输出 1&#xff0c;否则输出 0。 1582 年以来&#xff0c;闰年的定义&#xff1a; 普通闰年&#xff1a;公历年份是 4 的倍数&#xff0c;且不…

PMP证书可以挂靠吗?怎么挂靠?

哈喽学弟学妹们&#xff0c;作为过来人&#xff0c;今天想跟大家聊聊 PMP 证书挂靠这事儿 —— 可能不少准备考或者刚考完的同学都琢磨过&#xff0c;但学长得跟你们交个底&#xff1a;这事儿真不行&#xff0c;更别提啥挂靠费了。先说说 PMP 证书本身哈&#xff0c;它是美国 P…

91-基于Spark的空气质量数据分析可视化系统

基于Spark的空气质量数据分析可视化系统设计与实现 项目概述 本项目是一个基于Apache Spark的大数据分析和可视化系统&#xff0c;专门用于空气质量数据的采集、分析、预测和可视化展示。系统采用分布式计算架构&#xff0c;结合机器学习算法&#xff0c;实现了对全国12个主要…

leetcode 2419. 按位与最大的最长子数组 中等

给你一个长度为 n 的整数数组 nums 。考虑 nums 中进行 按位与&#xff08;bitwise AND&#xff09;运算得到的值 最大 的 非空 子数组。换句话说&#xff0c;令 k 是 nums 任意 子数组执行按位与运算所能得到的最大值。那么&#xff0c;只需要考虑那些执行一次按位与运算后等于…

Git 命令使用指南:从入门到进阶

目录1. Git 基本操作1.1 添加文件到暂存区1.2 提交更改到本地仓库1.3 查看工作区状态1.4 查看提交历史1.5 查看引用日志&#xff08;包括已删除的记录&#xff09;2. 版本回退与撤销2.1 版本回退2.2 查看已删除的提交记录3. 分支管理3.1 查看分支3.2 创建并切换到新分支3.3 合并…

SQL数据库连接Python实战:疫情数据指挥中心搭建指南

SQL数据库连接Python实战&#xff1a;疫情数据指挥中心搭建指南从WHO数据集到实时仪表盘&#xff0c;构建工业级疫情监控系统一、疫情数据指挥中心&#xff1a;全球健康危机的中枢神经​​疫情数据价值​​&#xff1a;全球每日新增病例&#xff1a;50万疫苗接种数据&#xff1…

参赛单位条件放宽!2025年“数据要素 ×”大赛福建分赛厦门赛区赛事有新调整

各位伙伴们 想抓住数据价值机遇 在行业赛场上崭露头角吗&#xff1f; 2025年“数据要素”大赛 福建分赛厦门赛区已启动 这份超全赛事解读 带你一站式摸清参赛关键&#xff01; 01 参赛单位要求放宽 经省分赛组委会与国家赛事组委会沟通&#xff0c;不具有独立法人资格的…

BasicAuthenticationFilter处理 HTTP 基本认证(Basic Authentication)的核心过滤器详解

BasicAuthenticationFilter处理 HTTP 基本认证&#xff08;Basic Authentication&#xff09;的核心过滤器详解在 Spring Security 中&#xff0c;BasicAuthenticationFilter 是​​处理 HTTP 基本认证&#xff08;Basic Authentication&#xff09;的核心过滤器​​&#xff0…

Next.js 中使用 MongoDB 完整指南

1. 安装依赖npm install mongodb # 或者使用 mongoose&#xff08;ODM&#xff09; npm install mongoose2. 数据库连接配置使用原生 MongoDB 驱动创建 lib/mongodb.js 文件&#xff1a;import { MongoClient } from mongodbconst uri process.env.MONGODB_URI const options …

嵌入式系统教学范式演进:云端仿真平台如何重构温湿度监测实验教学

在嵌入式系统开发的教学中&#xff0c;环境温湿度监测实验是经典的入门项目。它涉及传感器原理、外设驱动、数据采集和通信协议等核心知识点。然而传统实验模式面临硬件成本高、调试周期长、设备易损坏等痛点。学生往往因接线错误或代码bug导致传感器或开发板烧毁&#xff0c;不…

1.6万 Star 的流行容器云平台停止开源

什么是 KubeSphere &#xff1f; KubeSphere 是面向云原生应用的容器混合云。 KubeSphere 愿景是打造一个以 Kubernetes 为内核的云原生分布式操作系统&#xff0c;它的架构可以非常方便地使第三方应用与云原生生态组件进行即插即用&#xff08;plug-and-play&#xff09;的集成…

广东省省考备考(第六十三天8.1)——资料分析、数量(强化训练)

资料分析 错题解析解析解析今日题目正确率&#xff1a;80% 数量关系&#xff1a;数学运算 错题解析解析标记题解析解析解析今日题目正确率&#xff1a;87%

Agents-SDK智能体开发[3]之多Agent执行流程

文章目录说明Agents SDK基础Handoffs功能实现Handoffs简单示例&#x1f31f; 运行结果整理&#x1f4dd; 执行过程概述&#x1f4cb; 运行结果事件一&#xff1a;分诊智能体创建转交请求事件事件二&#xff1a;转交响应事件事件三&#xff1a;目标 Agent 响应请求并完成任务改进…

深度揭秘端口映射:原理、场景、路由映射故障,与内网IP端口映射外网工具的选择

内网设备连不上外网&#xff1f;本地网络如何设置端口映射提供互联网服务&#xff1f;路由器端口映射失败怎么办&#xff1f;没有公网IP如何做端口映射&#xff1f;在网络通信领域&#xff0c;端口映射是一项至关重要的技术。在内部网络环境中&#xff0c;每一台设备都被分配了…

协作机器人掀起工厂革命:码垛场景如何用数据重塑制造业命脉?

在长三角某食品包装工厂的深夜生产线上&#xff0c;大视协作码垛机器人正在有序码垛&#xff0c;动作比码垛老师傅更精准。系统推送的实时能耗报表直接显示在厂长手机上&#xff0c;整厂能耗同比下降约32%。这不是魔法&#xff0c;这是"一切数据业务化、业务数据化"在…

LeetCode 刷题【24. 两两交换链表中的节点、25. K 个一组翻转链表】

24. 两两交换链表中的节点 自己做 解&#xff1a;直接置换 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(i…

多线程向设备发送数据

需求:做一个部门授权&#xff0c;可以把所选择部门下面的所有人的人脸信息传到设备组里(多个设备)&#xff0c;问题在于图片是通过Base64处理之后的&#xff0c;会导致文件名非常长&#xff0c;如果一次性传很多数据就会超过设备的最长请求长度&#xff0c;如果不用Base64处理的…