C++中指针类型、引用类型、值类型

news2024/11/25 18:51:49

定义: 

1. * 的声明:指针类型

  • 声明方式MyClass* obj; 是一个 指针类型,表示 obj 是一个指针,可以指向 MyClass 类型的对象。

  • 指针特点

    • 指针存储的是对象的地址,可以为空(nullptr),也可以重新指向其他对象。
    • 指针需要通过 -> 运算符来访问对象的成员。
    • 指针需要手动管理内存(如果是通过 new 动态分配的)。
    • 例如:

      MyClass* obj = new MyClass(); // obj 是一个指向 MyClass 的指针 obj->someMethod(); // 通过指针访问方法 delete obj; // 手动释放内存

  • 适用场景

    • 当你需要动态分配内存时使用指针(比如在堆上分配对象)。
    • 当你需要在函数之间传递对象引用而不是对象副本时。

2. 不带 * 的声明:值类型

  • 声明方式MyClass obj; 是一个 值类型,表示 objMyClass 类型的对象,存储在栈上。
  • 值类型特点
    • 值类型对象会在栈上分配内存,其生命周期由作用域决定,当作用域结束时,内存会自动释放。
    • 通过 . 运算符来访问对象的成员。
    • 例如:

      MyClass obj; // obj 是一个 MyClass 类型的对象,存储在栈上 obj.someMethod(); // 通过对象访问方法

  • 适用场景
    • 当对象的生命周期明确、作用域有限时,使用值类型更简单和安全。
    • 避免不必要的动态内存分配,可以提高性能。

3. 引用类型:使用 & 符号

  • 引用类型与指针类似,但在语法和使用上有不同的特点。

  • 声明方式MyClass& objRef = obj; 表示 objRef 是一个对 obj 的引用。

  • 引用特点

    • 引用本质上是对象的别名,必须在定义时初始化,且无法更改其引用的对象。
    • 不能为 nullptr,也没有类似指针的 *-> 运算符。
    • 通常用于函数参数或返回值,以避免拷贝对象而是引用现有对象。
    • 例如:

      MyClass obj; // 正常创建一个 MyClass 对象 MyClass& objRef = obj; // 创建一个对 obj 的引用 objRef.someMethod(); // 通过引用访问方法,与 obj.someMethod() 等价

  • 适用场景

    • 当你希望函数参数传递时避免拷贝,但又不希望管理指针时,可以使用引用。
    • 作为函数返回值时,可以用于返回一个现有对象的别名。

生命周期的变化: 

示例代码:

class MyClass {
public:
	MyClass(const std::string& name) : name(name) {
		std::cout << "Creat(创建) called for: " << name << std::endl;
	}

	~MyClass() {
		std::cout << "destory(销毁) called for: " << name << std::endl;
	}

	void greet() const {
		std::cout << "Hello, my name is " << name << "!" << std::endl;
	}

private:
	std::string name;
};

void demonstratePointer() {
	MyClass* ptrObj = new MyClass("PointerObject"); // 指针类型
	ptrObj->greet();                              // 通过指针访问成员
	delete ptrObj;                                // 释放内存
}

void demonstrateValue() {
	MyClass valObj("ValueObject");                // 值类型
	valObj.greet();                               // 通过值对象访问成员
												  // valObj 的析构函数在函数结束时被自动调用
}

void demonstrateReference() {
	MyClass refObj("ReferenceObject");            // 创建值对象
	MyClass& ref = refObj;                        // 引用类型
	ref.greet();                                  // 通过引用访问成员
												  // refObj 的析构函数在函数结束时被自动调用
}


int main()
{

	demonstratePointer();    // 演示指针类型
	demonstrateValue();      // 演示值类型
	demonstrateReference();  // 演示引用类型

	system("pause");
    return 0;
}

运行结果如下: 

 

说明:

MyClass: 

  • 包含一个构造函数、析构函数和一个成员函数 greet
  • 构造函数打印对象的名称,析构函数在对象销毁时打印一条消息。

 demonstratePointer 函数(指针类型)

  • 创建一个 MyClass 对象的指针 ptrObj,在堆上动态分配内存。
  • 使用 -> 运算符调用 greet 方法,并在最后使用 delete 释放内存。
  • 对于动态分配的对象,则需要手动释放资源。

 demonstrateValue 函数(值类型)

  • 创建一个 MyClass 对象 valObj,在栈上分配内存。
  • 直接调用 greet 方法,函数结束时自动调用析构函数。
  • 对于栈对象,生命周期是自动管理的;

demonstrateReference 函数(引用类型)

  • 创建一个 MyClass 对象 refObj,并通过引用 ref 引用该对象。
  • 通过引用调用 greet 方法,refObj 的析构函数将在函数结束时自动调用。
  • 引用类型提供了对象的别名,它的资源释放遵循与值类型相同的规则,都是由绑定的对象来管理。使用引用可以避免不必要的对象拷贝,同时也需要注意引用对象的生命周期。

 异常案例:

1.悬空引用

MyClass& createObject() {
    MyClass obj("CreatedObject"); // 普通局部对象,不是静态的
    return obj; // 返回对局部对象的引用(不安全)
}

int main() {
    MyClass& newObj = createObject(); 
    newObj.greet(); // 这里可能会出现未定义行为
    return 0;
}

objcreateObject 函数中的局部对象。当函数返回时,obj 的生命周期结束并被销毁。然而,函数返回了一个对 obj 的引用,这在 newObj 中保存,但此时 obj 已经不存在了。调用 newObj.greet() 会访问已经被释放的内存,导致 未定义行为,可能会导致程序崩溃或其他不可预期的结果。

改进方案1:可以在堆上创建对象,并返回对象的指针。但这需要调用者在适当的时候手动释放内存。

MyClass* createObject() {
    MyClass* obj = new MyClass("CreatedObject"); // 在堆上分配
    return obj; // 返回指针
}

int main() {
    MyClass* newObj = createObject(); 
    newObj->greet(); // 使用指针访问
    delete newObj;   // 记得释放内存,避免内存泄漏
    return 0;
}

改进方案2:如果 MyClass 是可以被复制的,可以直接返回对象本身。C++ 编译器会优化返回对象的过程,不会产生多余的拷贝。

MyClass createObject() {
    MyClass obj("CreatedObject");
    return obj; // 返回对象(编译器可能优化为返回值优化)
}

int main() {
    MyClass newObj = createObject(); 
    newObj.greet(); // 正常使用,无需担心对象被销毁
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新的Midjourney就是一个增强版的Photoshop,你现在可以轻松的用它换衣服、换发型了

好久没有聊 Midjourney 了&#xff0c;昨晚他们发布了一项引人注目的新功能&#xff1a;AI 图像编辑&#xff0c;一个基于网页的加强版的 Photoshop 呼之欲出&#xff0c;让我大为震撼&#xff0c;也让用户们赞叹不已。 基于现有图像进行参考&#xff0c;进而生成新的图片&…

谈一谈 Netty 的内存管理 —— 且看 Netty 如何实现 Java 版的 Jemalloc

本文基于 Netty 4.1.112.Final 版本进行讨论 在之前的 Netty 系列中&#xff0c;笔者是以 4.1.56.Final 版本为基础和大家讨论的&#xff0c;那么从本文开始&#xff0c;笔者将用最新版本 4.1.112.Final 对 Netty 的相关设计展开解析&#xff0c;之所以这么做的原因是 Netty 的…

学习--四元数介绍

2022年的草稿箱里的一篇 四元数由William Rowan Hamilton发现 定义与复数类似&#xff0c;有三个虚部 q a b i c j d k qabicjdk qabicjdk 其中 i 2 j 2 k 2 i j k − 1 i^2j^2k^2ijk-1 i2j2k2ijk−1 四元数的模长&#xff1a; 四维的长度 加减法&#xff1a; 对应分…

十年编程路,一生踏征途

时光荏苒流逝&#xff0c;白驹匆匆过隙&#xff0c;不知不觉间&#xff0c;我已经在程序开发这条道路上走过了整整十年。从最初的求学&#xff0c;到如今成为一名较为资深的职业开发者&#xff0c;这一路充满了挑战、学习、成长与感动。在这1024程序员节的特殊时刻&#xff0c;…

信息安全工程师(69)数字水印技术与应用

前言 数字水印技术是一种在数字媒体中嵌入特定信息的技术&#xff0c;这些信息可以是版权信息、元数据等。 一、数字水印技术的定义与原理 数字水印技术&#xff08;Digital Watermarking&#xff09;是将一些标识信息&#xff08;即数字水印&#xff09;直接嵌入数字载体&…

发布者Publisher、订阅者Subscriber的编程实现+自定义话题消息编程实现

1.发布者Publisher的编程实现 1.创建功能包 cd ~/catkin_ws/src catkin_create_pkg learning_topic rospy roscpp std_msgs geometry_msgs turtlesim 2.创建发布者代码&#xff08;C&#xff09; ——文件名为velocity_publisher.cp /*************************************…

【Vue】word / excel / ppt / pdf / 视频(mp4,mov) 预览

文件预览 Vue3一. word二. excel三. ppt四. pdf4.1 vue-pdf-embed4.2 iframe 五. 视频六&#xff1a;扩展——kkFileView Vue3 一. word 安装&#xff1a;npm install docx-preview父页面 <template><div><DocPreviewv-if"filePath.includes(docx)"…

E. Sakurako, Kosuke, and the Permutation (置换环) Codeforces Round 981 (Div. 3)

哈哈哈, 之前做过一道置换环的题目, 但是当时不知道这是置换环 昨天写这道题目时老眼昏花, 读错题目, 如今一朝有悟,甄至化境 原题 E. Sakurako, Kosuke, and the Permutation 思路 这道题目与排序的不同在于, 如果第 i 个数不等于 i, 但是第 i 个数指向的数等于 i, 那么也…

基于RabbitMQ,Redis,Redisson,RocketMQ四种技术实现订单延时关闭功能及其相关优缺点介绍(以12306为主题)

目录 1. 延迟关闭订单 1.1 订单延时关闭功能技术选型 1.1.1 定时任务 1.1.2 RabbitMQ 1.1.3 Redis 过期监听 1.1.4 Redisson 1.1.5 RocketMQ 1.2 RocketMQ订单延时关闭发送方实现 1.3 RocketMQ订单延时关闭的消费方实现 1. 延迟关闭订单 用户发起订单后&#xff0c;如…

基于SSM的BBS社区论坛系统源码

运行环境&#xff1a;ideamysql5.7jdk8maven 使用技术&#xff1a;ssmmysqlshirolayui 功能模块&#xff1a;用户管理、模板管理、帖子管理、公告管理、权限管理等

使用 v-html 指令渲染的标签, 标签内绑定的 click 事件不生效

背景 在项目开发中&#xff0c;实现用户友好的输入交互是提升用户体验的关键之一。例如&#xff0c;在客服对话框中&#xff0c;其中有包含多个快捷选项用于快速问答&#xff0c;每个快捷选项都是一个可点击的按钮&#xff0c;并需要绑定点击事件来执行相应操作。然而&#xf…

Android Junit 单元测试 | 依赖配置和编译报错解决

问题 为什么在依赖中添加了testImplement在build APK的时候还是会报错&#xff1f;是因为没有识别到test文件夹是test源代码路径吗&#xff1f; 最常见的配置有: implementation - 所有源代码集(包括test源代码集)中都有该依赖库.testImplementation - 依赖关系仅在test源代码…

【CSS in Depth 2 精译_054】8.2 CSS 层叠图层(cascade layer)的推荐组织方案

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 【第三部分 现代 CSS 代码组织】 ✔️【第八章 层叠图层及其嵌套】 ✔️ 8.1 用 layer 图层来操控层叠规则&#xff08;上篇&#xff09; 8.1.1 图层的定义&#xff08;上篇&#xff09;8.1.2 图层的…

华为云实战杂记

配置nginx服务器 首先我们拿到一台服务器时&#xff0c;并不知道系统是否存在Nginx我们可以在Linux命令行执行如下命令查看 find / -name nginx* find / -name nginx* 查找所有名字以nginx开头的文件或者目录&#xff0c;我们看看系统里面都有哪些文件先&#xff0c;这样可以快…

Linux系统安装Redis详细操作步骤(二进制发布包安装方式)

安装方式介绍 在Linux系统中&#xff0c;安装软件的方式主要有四种&#xff0c;这四种安装方式的特点如下&#xff1a; 安装方式特点二进制发布包安装软件已经针对具体平台编译打包发布&#xff0c;只要解压&#xff0c;修改配置即可rpm安装软件已经按照redhat的包管理规范进…

雷池社区版有多个防护站点监听在同一个端口上,匹配顺序是怎么样的

如果域名处填写的分别为 IP 与域名&#xff0c;那么当使用进行 IP 请求时&#xff0c;则将会命中第一个配置的站点 以上图为例&#xff0c;如果用户使用 IP 访问&#xff0c;命中 example.com。 如果域名处填写的分别为域名与泛域名&#xff0c;除非准确命中域名&#xff0c;否…

NGINX 保护 Web 应用安全之基于 IP 地址的访问

根据客户端的 IP 地址控制访问 使用 HTTP 或 stream 访问模块控制对受保护资源的访问&#xff1a; location /admin/ { deny 10.0.0.1; allow 10.0.0.0/20; allow 2001:0db8::/32; deny all; } } 给定的 location 代码块允许来自 10.0.0.0/20 中的任何 IPv4 地址访问&#xf…

UE4_Niagara基础实例—9、使用条带渲染器来制作闪电

效果图&#xff1a; 一、通过模板Static Beam来熟悉条带渲染器 从Static Beam发射器新建niagara系统&#xff0c;更名为NS_StaticBeam。 打开粒子系统&#xff0c;界面如下&#xff1a; Beam Emitter Setup模块可以设置条带的开始点、结束点和切线。 我们就可以通过这个Beam E…

自动化测试:等待方式

在自动化测试中&#xff0c;等待是一个重要的技术&#xff0c;用于处理页面加载、元素定位、元素状态改变等延迟问题。 等待能够确保在条件满足后再进行后续操作&#xff0c;提高自动化测试的稳定性以及可靠性。 等待方式&#xff1a;显示等待、隐式等待、线程睡眠 1. 显式等…

【python】OpenCV—WaterShed Algorithm(1)

文章目录 1、功能描述2、代码实现3、完整代码4、效果展示5、涉及到的库函数5.1、cv2.pyrMeanShiftFiltering5.2、cv2.morphologyEx5.3、cv2.distanceTransform5.4、cv2.normalize5.5、cv2.watershed 6、参考 1、功能描述 基于分水岭算法对图片进行分割 分水岭分割算法&#x…