天天看点

C++指针和引用 --C++前言理论知识及示例代码何时用指针,何时用引用?结束语

前言

因为基础不好,C++的指针和引用,我一直分不大清,但是老被问区别啊,两者用的场合啊什么的。所以趁着最近有时间,特意去网上查资料,看公开课等,进行学习,最终整理了这篇博客。

理论知识及示例代码

学过C++的我们知道访问对象的方式有三种:引用、指针和变量。变量这个不用解释,就是直接访问;而指针和引用是什么?之前我觉得他俩差不多,都可对原对象进行操作,在函数传参中属于地址传递。我比较常用指针,可能是C语言用习惯了(C语言是没有引用这一概念的),但是错误使用指针是有安全隐患的(内存泄漏、重复释放、越界等)。所以还是均衡的使用吧,下面只介绍指针和引用。

指针

一级指针和二级指针

指针本质上很特殊的数据,它里面存储的是内存地址数据,指向这一块内存位置。又因为他是数据,故它还能进行部分运算:算数运算(常用的是指针加减,即指针的指向偏转)、关系运算和赋值运算。故可以说指针是一种特别灵活的访问数据的方式。举个例子:

int num=10;
    int* pVal=#
    qDebug()<<QString("num的地址是")<<&num;
    qDebug()<<QString("pVal指向的数据的值是")<<*pVal;
    qDebug()<<QString("pVal指向的地址是")<<pVal;
    qDebug()<<QString("pVal的地址是")<<&pVal;
           

打印出来的结果是这样的。

C++指针和引用 --C++前言理论知识及示例代码何时用指针,何时用引用?结束语

 可以看出内存间的位置大概是这样的,如下图。

C++指针和引用 --C++前言理论知识及示例代码何时用指针,何时用引用?结束语

这是较为基础简单的一级指针操作:指向某块内存,进行某些操作,比如动态内存分配(new和delete);但是有时候一级指针无法满足我们的需求,如在函数中修改指针的指向,见下例子

int a=10;
int b=100;
int *q;

void func(int* p)
{
    cout<<"func:&p="<<&p<<",p="<<p<<",*p="<<*p<<endl;
    p=&b;
    *p=101;
    cout<<"func::&p="<<&p<<",p="<<p<<",*p="<<*p<<endl;
}

int main()
{
    cout<<"&a="<<&a<<",&b="<<&b<<",&q="<<&q<<endl;
    q=&a;
    cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;
    cout<<"-----------------------------------------"<<endl;
    func(q);
    cout<<"-----------------------------------------"<<endl;
    cout<<"b="<<b<<endl;
    cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;

    return 0;
}
           

打印出来的结果是:

C++指针和引用 --C++前言理论知识及示例代码何时用指针,何时用引用?结束语

可以看到:当我们把指针q指向a后,再将指针q作为参数传入函数func后,发现指针的指向并没有发生变化。从打印的结果可看出 指针q和输入参数p,所占地址并不同,只是值被拷贝了,当函数结束后,指针p也就被释放了,所以函数并没有改变指针的指向。当然若只是改变指向的值(解引用的值),还是变化的。

为了解决这一问题,我们用到了二级指针,如下:

void func2(int**p)
{
    cout<<"func2:&p="<<&p<<",p="<<p<<endl;
    *p=&b;
    cout<<"func2:&p="<<&p<<",p="<<p<<endl;
}

int main()
{    
    cout<<"&a="<<&a<<",&b="<<&b<<",&q="<<&q<<endl;
    q=&a;
    cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;
    func2(&q);
    cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;


    return 0;
}
           

 打印结果:

C++指针和引用 --C++前言理论知识及示例代码何时用指针,何时用引用?结束语

 二级指针,可以这么理解就是指向一级指针,值是一级指针的地址。像上面的一级指针的例子,我们无法改变指针的指向,但可以改变指向的值,那也是说,当我们将一级指针的地址作为指针指向的的值,就可改变其指向,这就解决了改变指针指向的问题。函数func2()内存指向如下图:

C++指针和引用 --C++前言理论知识及示例代码何时用指针,何时用引用?结束语

智能指针

有时候为指针新开辟了一块内存,但忘了释放,这就造成了内存的泄漏。所以就有了智能指针的出现:使内存管理方面更加安全。它分为三类(这部分内容我涉足不多,下次补充):

unique_ptr:不允许多个指针共享资源,可以用标准库中的move函数转移指针

shared-ptr:多个指针共享资源

weak_ptr:可复制shared_ptr,但其构造或释放多资源不产生影响

引用(Reference)

引用,是C++中一个新型的数据类型,C语言中是没有的。引用可以看做是目标对象的一个别名,对引用操作其实是对目标对象操作。这样看好像指针的作用,虽两者有区别(不然也不会出现这个新的数据类型),但其实引用可以看成是特殊的指针。

声明和定义:(1)对于局部变量和全局变量,定义时,则必须与目标对象绑定,且无法解绑,与指针不同,不能为空。

                           大概形式为:type& refname=name;

                      (2)在参数列表和成员变量中,可以先声明不绑定对象,然后在方法调用时或构造函数初始化时绑定。

                           声明时形式为:type& refname;

引用的规则(Rules of references)

(1)定义时必须初始化

(2)初始化创建绑定

int x=3;
int &y=x;
const int& z=x;

//As a function argument
void f(int& x);
f(i); //initialized when function is called
           

(3)在运行时不能改变绑定,不像指针似的

(4)目标引用变量赋值时,改变被引用的对象(双向传递),如

    int &y=x;
    y=12; //changes value of x
           

(5) 别引用的目标对象必须有位置(内存空间)

vioid func(int& );
func(i*3); //warning or error
           

比较指针和引用(Points vs. References)

References Points
can't be null can  be set to null
are dependent on  an exsting variable,they are an alias for a variable pointer is independent of existing objects
can't change to a new "address" location can change to point to a different address

限制(Restrictions)

(1)没有引用的引用

(2)没有指向引用的指针

int &* p; //illegal *p的类型是int& (引用)

//Reference to pointer is ok
void f(int* &p) 
           

(3)没有引用的数组

何时用指针,何时用引用?

通过上面对指针和引用的理论介绍,对两者也有了个系统的理解。两者有很多相似的地方,那若作为参数传递,何时用指针,又何时用引用?下面进行叙述。

怎么说呢,指针和引用很多时候都可以通用的,能够用引用的场合必定可用指针代替,但能用指针的时候,未必能用引用代替。相对来说,指针很灵活(可以为空,可以改变指向),这个是他的优点,也是他的缺点(有安全隐患,运行效率相对引用来说低)。引用呢,则很稳定安全。

参数传递

这部分是看到一篇很不错的博客(https://blog.csdn.net/hbtj_1216/article/details/56843014),先贴这里了

何时使用引用参数

使用引用参数的主要原因有两个:

(1)程序员能够修改调用函数中的数据对象。

(2)通过传递引用而不是整个数据对象,可以提高程序的运行速度。

什么时候使用指针?什么时候使用引用?什么时候应该按值传递?

下面是一些指导原则:

对于那些函数,它们只使用传递过来的值,而不对值进行修改。      

(1)如果数据对象很小,如内置数据类型或小型结构,使用按值传递。 

(2)如果数据对象是数组,则使用指向const的指针。

(3)如果数据对象是较大的结构,则使用const指针或者const引用,以提高程序的效率。  

(4)如果数据对象是类对象,则使用const引用。因此,传递类对象参数的标准方式是按引用传递。

对于那些函数,它们需要修改传递过来的值。     

(1)如果数据对象是内置数据类型,则使用指针。     

(2)如果数据对象是数组,则只能使用指针。       

(3)如果数据对象是结构。则使用指针或者引用。     

(4)如果数据对象是类对象,则使用引用。

结束语

感觉基础没打好,当程序员真难受,继续努力吧!

继续阅读