当前位置: 首页 > news >正文

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

定义: 

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.mrgr.cn/news/57623.html

相关文章:

  • sql数据库增删改数据(DML)
  • Meta推出OMat24:AI驱动材料发现迈向新纪元
  • 玩转PyCharm:常用操作和快捷键
  • 如何防止服务器被渗透攻击
  • 材质变体 PSO学习笔记
  • 计算机网络基础进阶
  • 面试必备:RabbitMQ与Kafka核心知识点总结
  • 使用 SpaCy 和 NLTK 进行文本处理与切片详解
  • 中酱集团:黑松露酱油,天然配方定义健康生活
  • 【golang】学习文档整理
  • 11 怎么给字符串字段加索引?
  • js 基础补充3
  • 【面试题】如果 Redis 遇到 Hash 冲突了该怎么处理?
  • 代码随想录-哈希表-快乐数
  • 安装报错解决:No module named ‘quaternion‘
  • 无线路由器设置成交换机(补充)
  • kotlin等待异步任务完成
  • 植入感体感游戏
  • 嵌入式编程守则
  • 计算机xapofx1_5.dll丢失怎么办,分享5种有效的解决方法
  • 基于SpringBoot的在线拍卖系统【源码+论文】
  • 4466 最长连续重复字符(longest)
  • 数据库中DDL、DML、DCL的区别是什么
  • 免费ppt模板从哪找?盘点精美ppt模板下载方法
  • 迅策科技累亏3.63亿:应收账款周转天数飙升,净收入留存率大幅下滑
  • PE(市盈率)、PB(市净率)、PS(市销率)和PCF(市现率)评估股票是否具有投资价值的重要指标