C++:引用和指针

目录

引用和指针的区别

指针类型

内存泄漏

智能指针

函数指针和指针函数


引用和指针的区别

引用

指针

定义和声明方式

int a = 5;

引用使用&符号进行定义和声明:int& ref = a;

指针使用*符号进行定义和声明:int* ptr = &a;

空值

引用不能为null,它必须在声明时初始化,并且一直引用同一个对象

指针可以为null,可以在声明后赋值为null,也可以指向不同的对象。

变量别名

引用是变量的别名,对引用的修改会影响原始变量的值。

指针是一个独立的变量,通过指针可以访问或修改所指向的变量。

语法和操作

引用在使用时不需要使用解引用操作符*,直接使用引用变量名即可。

指针需要使用解引用操作符*来访问所指向的值。

引用只有一级

指针可以有多级

sizeof引用得到的是引用所指向变量的大小

sizeof指针得到的是本指针的大小

本质

引用本质是一个指针,同样会占4字节内存

指针是具体变量,需要占用存储空间

示例如下:

#include 
using namespace std;
int main() {
    int a = 5;
    int& ref = a;  // 引用
    int* ptr = &a; // 指针

    ref = 10; // 修改引用的值会影响原始变量
    cout << "Value of a: " << a << endl; // 输出:10

    *ptr = 20; // 通过指针修改所指向的值会影响原始变量
    cout << "Value of a: " << a << endl; // 输出:20

    int b = 30;
    ptr = &b; // 指针可以指向不同的对象
    cout << "Value of *ptr: " << *ptr << endl; // 输出:30

    // ref = nullptr; // 错误,引用不能为null

    int* nullPtr = nullptr; // 指针可以为null
    if (nullPtr == nullptr) {
        cout << "nullPtr is null" << endl;
    }
    return 0;
}

指针类型

int *p[10]

int *p[10]表示指针数组,强调数组概念,是一个数组变量,数组大小为10,数组内每个元素都是指向int类型的指针变量。

int (*p)[10]

int (*p)[10]表示数组指针,强调是指针,只有一个变量,是指针类型,不过指向的是一个int类型的数组,这个数组大小是10

int *p(int)

int *p(int)是函数声明,函数名是p,参数是int类型的,返回值是int *类型的。

int (*p)(int)

int (*p)(int)是函数指针,强调是指针,该指针指向的函数具有int类型参数,并且返回值是int类型的。

内存泄漏

        用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。

内存泄漏的常见情况包括以下几种:

  1. 未释放堆上分配的内存:当使用new运算符在堆上分配内存后,应使用delete运算符释放内存。如果忘记释放分配的内存,将导致内存泄漏。
  2. 未释放动态数组的内存:如果使用new[]运算符分配了一个动态数组,应使用delete[]运算符释放内存。忘记释放动态数组的内存同样会导致内存泄漏。
  3. 丢失指向堆上分配内存的指针:如果一个指向堆上分配的内存的指针丢失了,那么将无法释放这块内存,从而导致内存泄漏。

解决方法:

  1. 使用智能指针:C++中的智能指针(如std::unique_ptr和std::shared_ptr)提供了自动管理内存的能力。使用智能指针可以在对象不再需要时自动释放内存,从而避免内存泄漏。
  2. 显式释放内存:在使用new和new[]分配内存后,确保在不再需要时显式使用delete和delete[]释放内存。注意在适当的时候释放动态分配的内存,避免内存泄漏。
  3. 慎使用动态内存分配:在设计程序时,考虑使用自动变量或固定大小的数据结构,避免频繁地进行动态内存分配,从而减少内存泄漏的机会。
  4. 使用RAII(资源获取即初始化)原则:RAII是一种资源管理的编程技术,通过在对象的构造函数中分配资源,在对象的析构函数中释放资源,来确保资源的正确释放。使用RAII可以有效地避免内存泄漏。
  5. 使用内存分析工具:使用内存分析工具(如Valgrind、Visual Leak Detector等)来检测和定位内存泄漏问题。这些工具可以帮助识别程序中的内存泄漏,并提供相关的调试信息。

智能指针

        智能指针是C++中用于管理动态分配内存的一种机制,它可以自动处理资源的分配和释放,从而避免了内存泄漏和空指针等问题。C++标准库提供了两种主要的智能指针类型:std::unique_ptr(独占)和std::shared_ptr(共享)。

std::unique_ptr(独占智能指针)

std::shared_ptr (共享智能指针)

创建:std::unique_ptr ptr = std::make_unique(42);

创建:std::shared_ptr ptr = std::make_shared(42);

所有权管理:

同一时间只能有一个std::unique_ptr拥有指向对象的所有权。它不能被复制或拷贝,只能进行所有权的转移。当std::unique_ptr超出作用域或被显式释放时,它会自动释放所管理的对象。

转移所有权:std::unique_ptr ptr2 = std::move(ptr);

所有权管理:

支持复制和拷贝操作,即可以直接进行复制构造和赋值操作,从而实现对象的共享所有权。

共享所有权:std::shared_ptr ptr2 = ptr;(使用引用计数来追踪有多少个)

访问指针所指向的对象:int value = *ptr2;

访问指针所指向的对象:int value = *ptr;

显式释放内存(显式和自动):std::unique_ptr会在超出作用域时自动释放所管理的对象,也可以显式地使用reset()函数释放对象,例如:ptr2.reset();

自动释放内存:当最后一个共享指针超出作用域时,std::shared_ptr会自动释放所管理的对象。

由于独占所有权的特性,std::unique_ptr不会引起循环引用的问题

解决循环引用:

存在循环引用时,std::shared_ptr会导致资源无法释放,从而导致内存泄漏。为了解决这个问题,可以使用std::weak_ptr来打破循环引用。

内存开销:轻量

由于需要维护引用计数,

shared_ptr比unique_ptr更重量级,因为它需要额外的开销来跟踪和更新引用计数。

函数指针和指针函数

函数指针(function pointer)是一个指向函数的指针变量。可以把函数指针理解为指向函数的地址,通过函数指针可以调用相应的函数。

指针函数(pointer to a function)是一个返回指针的函数。指针函数声明时,需要使用*表示返回类型是指针类型。

#include

int add(int a, int b) {

    return a + b;

}

int main() {

    int (*ptr)(int, int); // 函数指针的声明

    ptr = add; // 函数指针指向add函数

    int result = ptr(3, 4); // 通过函数指针调用add函数

    printf("Result: %d\n", result);

    return 0;

}

#include

int* getArray() {

    static int arr[5] = {1, 2, 3, 4, 5};

    return arr;

}

int main() {

    int* (*ptr)(); // 指针函数的声明

    ptr = getArray; // 指针函数指向getArray函数

    int* array = ptr(); // 通过指针函数调用getArray函数

    for (int i = 0; i < 5; i++) {

        printf("%d ", array[i]);

    }

    printf("\n");

    return 0;

}

ptr是一个指向函数的指针,它指向add函数。通过函数指针ptr可以调用add函数。

ptr是一个指向函数的指针,它指向getArray函数。通过指针函数ptr可以调用getArray函数,并返回一个指向数组的指针。

函数指针是指向函数的指针变量,可以通过函数指针调用相应的函数。

指针函数是一个返回指针的函数,其返回类型是指针类型。

你可能感兴趣的:(C++,c++)