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

指针和引用的区别

📃问题

指针和引用的区别

📝我的答案

首先,本质上指针是一个变量,存储的是内存地址;而引用只是一个别名,必须绑定到已有对象。

其次,指针可以不初始化,可以为空,也可以随时改变指向;而引用必须在定义时初始化,一旦绑定就不能改变,也没有空引用的概念。

在使用上,指针需要用星号解引用才能访问对象,比如 *p;引用则直接用变量名就能访问,更直观。

sizeof 的结果也不同,对指针执行 sizeof 得到的是指针自身的大小(通常 4 或 8 字节),而对引用执行 sizeof 得到的是所引用对象的大小。

在函数参数传递时,指针传递的是地址的副本,需要检查空指针;引用传递直接借用对象,更安全高效。

const 修饰也有区别,指针有 const 指针(T* const)和指向 const 的指针(const T)两种形式;引用只有指向 const 的引用(const T&)。

关于临时对象,const 引用可以绑定到临时对象并延长其生命周期,这在函数参数传递中特别有用。比如 void func(const string& s) 可以直接接受字符串字面量而不需要额外构造 string 对象。

在多态和继承方面,两者都能实现多态,但引用更安全,因为它保证指向有效对象,不需要像指针那样做空检查。

另外,在现代 C++ 中,右值引用(T&&)和移动语义的引入让引用的应用更加丰富。我们可以用它实现完美转发和移动语义,大幅提升性能。

从底层实现看,虽然引用通常被编译器实现为指针,但编译器会对引用添加更多优化假设,比如非空和可解引用的保证,这可能带来更好的性能。

最后,在实际项目中,我一般在需要表示'可能不存在'或'需要动态改变指向'的场景用指针,而在只需要避免拷贝开销、不涉及所有权转移的场景用引用。如果参数必须存在且只是借用,我用引用;如果参数可选或表达所有权转移,我用(智能)指针。现在有了智能指针,很多原来用裸指针的地方我都改用 unique_ptr 或 shared_ptr 了,这样可以通过类型系统表达设计意图,提高代码可读性和安全性。"

再来结合代码例子解释指针和引用的区别:

1. 本质区别:变量 vs 别名

int num = 10;
int* ptr = #     // 指针是变量,存储num的地址
int& ref = num;      // 引用是别名,ref就是num的另一个名字
*ptr = 20;           // 通过指针修改numref = 30;            // 通过引用修改numcout << num << endl; // 输出30

2. 初始化和重绑定

int* ptr1;           // 指针可以不初始化(不推荐)
ptr1 = nullptr;      // 可以为空
int a = 5, b = 10;
int* ptr2 = &a;      
ptr2 = &b;           // 可以改变指向
// int& ref;         // 错误:引用必须初始化
int& ref = a;        
// ref = &b;         // 错误:这不是重绑定,而是给a赋值b的值

3. 访问语法

int val = 100;
int* ptr = &val;
int& ref = val;
*ptr = 200;          // 指针需要解引用
ref = 300;           // 引用直接使用
cout << val << endl; // 输出300

4. sizeof结果

int num = 42;
int* ptr = &num;
int& ref = num;
cout << sizeof(ptr) << endl;  // 输出指针大小,如8(64位系统)
cout << sizeof(ref) << endl;  // 输出int大小,如4

5. 函数参数传递

// 使用指针
void incrementPtr(int* p) 
{if (p) {         // 需要检查空指针(*p)++;}
}
// 使用引用
void incrementRef(int& r) 
{r++;             // 不需要检查,更简洁
}int x = 10;
incrementPtr(&x);    // 传递地址
incrementPtr(nullptr); // 合法但不会做任何事
incrementRef(x);     // 直接传递对象
// incrementRef(10); // 错误:非const引用不能绑定到临时值

6. const修饰

int value = 5;
const int* p1 = &value;      // 指向const的指针(不能通过p1修改value)
// *p1 = 10;                 // 错误
p1 = nullptr;                // 可以改变p1指向
int* const p2 = &value;      // const指针(不能改变p2指向)
*p2 = 10;                    // 可以通过p2修改value
// p2 = nullptr;             // 错误
const int& r = value;        // 指向const的引用
// r = 10;                   // 错误:不能通过r修改value

7. 临时对象绑定

// 临时对象绑定到const引用,延长生命周期
const std::string& s = std::string("hello") + " world";
cout << s << endl;           // 安全,临时对象在s的作用域内有效
// 函数参数中的应用
void printLength(const std::string& str) 
{cout << str.size() << endl;
}
printLength("direct string");  // 不需要显式构造string

8. 多态和继承

class Base 
{
public:virtual void show() { cout << "Base class" << endl; }virtual ~Base() {}
};class Derived : public Base {
public:void show() override { cout << "Derived class" << endl; }
};// 使用引用实现多态
void polymorphicRef(Base& base) 
{base.show();     // 安全,一定有对象
}
// 使用指针实现多态void polymorphicPtr(Base* base) 
{if (base) {      // 需要检查base->show();}
}
Derived d;
polymorphicRef(d);   // 安全调用
polymorphicPtr(&d);  // 需要传地址
polymorphicPtr(nullptr); // 合法但不会调用show

9. 右值引用和移动语义

// 移动语义
void processVector(std::vector<int>&& vec) 
{// 可以"窃取"vec的资源,因为它是右值std::vector<int> internal = std::move(vec);// 处理internal...
}// 完美转发
template<typename T>
void relay(T&& arg) 
{process(std::forward<T>(arg)); // 保持值类别
}
std::vector<int> v{1, 2, 3};
processVector(std::move(v)); // v被移动,之后v为空

10. 实际项目中的选择

// 引用 - 当参数必须存在
void updateConfig(Config& config) 
{config.setValue("timeout", 30);
}
// 指针 - 当参数可能不存在
bool findUser(const string& username, User* result) {if (database.hasUser(username)) {*result = database.getUser(username);return true;}return false;
}
// 智能指针 - 表达所有权
class ResourceManager {
private:std::unique_ptr<Resource> resource; // 独占所有权std::shared_ptr<Logger> logger;     // 共享所有权
};
http://www.dtcms.com/a/326135.html

相关文章:

  • SQL中BETWEEN与IN的差异详解
  • Mybatis学习之缓存(九)
  • BM25算法记忆
  • 推荐中的在线学习
  • macos彻底删除vscode
  • Spring JDBC
  • 零基础AI编程开发微信小程序赚流量主广告实战
  • 6s081实验1
  • redis(2)-java客户端使用(IDEA基于springboot)
  • USB 基本描述符
  • Go 多进程编程-管道
  • C++方向知识汇总(三)
  • 面试实战 问题二十三 如何判断索引是否生效,什么样的sql会导致索引失效
  • git:分支
  • 3Ds Max的魔改利器:RailClone - 程序化建模的革命者
  • MySQL 经典练习 50 题(完美解答版,提供解题思路)
  • Spring Framework源码解析——DisposableBean
  • Oracle数据库中的Library cache lock和pin介绍
  • Java多线程并发控制:使用ReentrantLock实现生产者-消费者模型
  • js异步操作 Promise :fetch API 带来的网络请求变革—仙盟创梦IDE
  • 机器翻译:Bahdanau注意力和Luong注意力详解
  • 【浮点数存储】double类型注意点
  • 理解LangChain — Part 3:链式工作流与输出解析器
  • Notepad--:国产跨平台文本编辑器,Notepad++ 的理想替代方案
  • 写一篇Ping32和IP-Guard的对比,重点突出Ping32
  • 循环控制:break和continue用法
  • 鸿蒙flutter项目接入极光推送
  • Java项目基本流程(三)
  • Orange的运维学习日记--38.MariaDB详解与服务部署
  • linux安装和使用git