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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值