C++ 引用和指针的区别详解

文章详细对比了C++中的指针和引用,包括它们的定义、初始化、使用方式、作为函数参数和返回值时的不同。指针可以先定义后初始化,而引用必须在定义时绑定到一个对象且之后不能改变。引用提供了更安全的传递和修改对象的方式,避免了空指针和野指针的问题,适合用于函数参数和返回值以防止复制。同时,引用支持链式编程,而指针的灵活性更高,可以用于多级指针和数组元素。
摘要由CSDN通过智能技术生成

首先明确定义:

        指针 是一个变量,用来存放另一个变量的地址

        引用 是某一个变量别名

        引用分为左值引用和右值引用。

补充:

左值(loactor value),可以看作是存储在内存中的,有明确存储地址(可寻址)的数据;
右值(read value),指的是可以提供数据值的数据(不一定可以寻址,比如常量是存储于寄存器中的数据)。

        左值引用与指针有重叠部分,右值引用则属于另一部分,所以区别方面主要讨论左值引用和指针的区别(以下引用未说明则都是左值引用)。

        从定义可以看出,指针和引用都变量有关,那么我们可以从变量使用的角度分析差别。大致分为以下三个个角度:

  • 变量本身
  • 作为函数参数
  • 作为返回值

变量本身

  • 初始化:

        指针可以先定义再初始化。

        引用作为变量的别名,必须在定义的时候初始化,即引用到一个有效的对象。避免了空指针和野指针,使用时不用验证其合法性。

    // 初始化
    int a = 10;
    int* p1;            // 正确 未初始化 野指针
    int* p2 = nullptr;  // 正确 初始化为 空指针
    int* p3 = &a;       // 正确 p2 初始化为 a 的地址
//  int& r1;            // 错误 引用必须初始化  error: 'r1' declared as reference but not initialized
    int& r2 = a;        // 正确 r2 初始化为 a 的引用
  • 使用:

        操作引用与直接操作变量完全一样,指针需解引用。

    // 使用
    int b = 20;
    b = a;      // 变量
    b = *p3;    // 指针
    b = r2;     // 引用

        引用初始化后不可改变指向、指针可以改变。

    // 修改
    p1 = p3;    // 正确 修改 p1 指向,现在 p1 指向 a
    int& r3 = b;
    r3 = r2;    // 正确 修改 r3 的值为 r2, 即 b = a
    std::cout << "a=" << a << " b=" << b << std::endl;  // a=10 b=10

        可以有多级指针,没有多级引用。

    // 多级
    int** pp3 = &p3;        // 正确 pp3 为 a 的二级指针
    int*** ppp3 = &pp3;     // 正确 ppp3 为 a 的三级指针
//  int&& rr1 = r2;         // 错误 error:cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'
    int&& rr2 = 123;        // 正确 && 右值引用
//  int&&& rrr2 = rr2;      // 错误 error:cannot declare reference to 'int&&', which is not a typedef or a template type argument

函数参数

        在函数参数方面,引用可以避免拷贝。在函数内部,若想修改实参,直接修改引用即可。

        指针本身是一个变量,传参时指针会发生拷贝,在函数内修改形参指针的指向,不会改变实参指针的指向。

返回值

        在返回值方面,主要是可安全性和链式编程两方面。

        安全性:

        由于指针初始化后可以改变指向,如果程序设计不当,调用者可能修改预期以外的数据。引用初始化后不可修改指向,安全性会好一些。

std::vector<int> vec = {0,1,2,3,4,5,6,7,8,9};
// 返回对应下标元素的地址,给外面修改
int* rt_p(int pos) {
    // TODO 
    return &vec[pos];
}
int& rt_r(int pos) {
    // TODO 
    return vec[pos];
}
// 输出 vec
void printVec(){
    for (auto& i : vec) {
        std::cout << i << " ";
    }
    std::cout << std::endl;
}

void test3() {
    int* p = rt_p(0);   // 取得 0 号位置的元素
    p += 8;             // 本意是 *p += 3 结果改变了 p 的指向, p = &vec[8];
    // TODO             // 业务逻辑
    // ...
    *p += 5;             // 本意是对 0 号元素赋值,结果改变了 vec[8]
    printVec();         // 输出: 0 1 2 3 4 5 6 7 13 9
    int& r = rt_r(0);   // 取得 0 号位置的元素
    r += 3;             
    printVec();         // 输出: 3 1 2 3 4 5 6 7 13 9
}

        链式编程:

        返回当前对象的引用,实现成员函数、仿函数、操作符等的链式调用。

class A{
public:
    A(): m_a(0) {}
    // 成员函数
    A& add(const int v) {
        m_a += v;
        return *this;
    }
    // 仿函数
    A& operator()(const int v) {
        m_a += v;
        return *this;
    }
    // 操作符
    A& operator<<(const int v) {
        m_a += v;
        return *this;
    }

    int m_a;
};

void test4(){
    A a;
    a.add(1).add(2).add(3);             // 成员函数链式调用
    std::cout << a.m_a <<  std::endl;   // 6

    a.m_a = 0;
    a(1)(2)(3);                         // 仿函数链式调用
    std::cout << a.m_a <<  std::endl;   // 6

    a.m_a = 0;
    a << 1 << 2 << 3;                   // 操作符链式调用
    std::cout << a.m_a <<  std::endl;   // 6
}

        上面从变量本身、参数、返回值三个方面分析了引用和指针的区别,可以看出引用相比于指针有很多优点,但是其也有其缺点,简单叙述如下:

        引用作为一个变量的别名,相当于一个变量的附属品,没有独立性,任何操作都是与引用关联的对象相关的。指针作为一个独立的变量,其灵活性更高。

        指针可以作为放入数组的元素。

        指针自身也有地址,所以有多级指针,可以任意嵌套。

        指针可以改变指向,方便迭代计算。

涉及右值引用时可以参考这篇文章C++知识篇--右值引用_c++ 右值引用_煮雪品茶的博客-CSDN博客

        

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值