智能指针

  1. 智能指针分类: std::unique_ptr, std::shared_ptr, std::weak_ptr.使用它们需要包含头文件 <memory>.
  2. std::unique_ptr 是独占所有权的智能指针, 确保同一时间只能有一个 unique_ptr指向对象. 不能进行拷贝操作,但是可以通过移动操作将所有权转移给其它指针.
  3. std::shared_ptr 允许多个指针共享同一个对象的所有权,通过引用计数来管理共享资源,当最后一个指向该对象的指针被销毁时,对象会被自动释放.
  4. std::weak_ptr 是一种弱引用的智能指针, 不拥有对象的所有权, 不会影响对象的生命周期,通常用于解决循环引用问题.配合 std::shared_ptr 的使用,解决循环引用的问题.
  5. std::auto_ptr 是C++98标准引入的智能指针, 也实现了对对象的独占所有权, 但在复制操作时会移动资源.在C++11中被废弃.
  6. C++11 新增了 std::make_shared() 方法创建一个 std::shared_ptr 对象,却没有提供相应的 std::make_unique() 方法创建一个 std::unique_ptr 对象,这个方法直到 C++14 才被添加进来.
std::unique_ptr
std::shared_ptr
std::weak_ptr
std::auto_ptr
#include <iostream>
#include <memory>
#include <string>

// 一个简单的类,用来观察构造和析构
class Person {
public:
    Person(const std::string& name) : name_(name) {
        std::cout << "构造 Person: " << name_ << "\n";
    }
    ~Person() {
        std::cout << "析构 Person: " << name_ << "\n";
    }
    void sayHello() const {
        std::cout << "Hello, I am " << name_ << "\n";
    }
private:
    std::string name_;
};

// 接受 unique_ptr 的函数(通过移动)
void greet(std::unique_ptr<Person> p) {
    p->sayHello();
}

int main() {
    // 1. 创建 unique_ptr
    std::unique_ptr<Person> p1 = std::make_unique<Person>("Alice");
    p1->sayHello();

    // 2. 转移所有权(move)
    std::unique_ptr<Person> p2 = std::move(p1);
    if (!p1) {
        std::cout << "p1 已经失去所有权\n";
    }
    p2->sayHello();

    // 3. 将 unique_ptr 传递给函数(必须使用 std::move)
    greet(std::move(p2));
    if (!p2) {
        std::cout << "p2 也失去所有权了\n";
    }

    // 4. 自定义删除器
    auto deleter = [](Person* p) {
        std::cout << "使用自定义删除器释放 Person\n";
        delete p;
    };
    std::unique_ptr<Person, decltype(deleter)> p3(new Person("Bob"), deleter);

    p3->sayHello();

    // main 结束时,p3 会自动调用自定义删除器
    return 0;
}
/**
Output:
构造 Person: Alice
Hello, I am Alice
p1 已经失去所有权
Hello, I am Alice
Hello, I am Alice
析构 Person: Alice
p2 也失去所有权了
构造 Person: Bob
Hello, I am Bob
使用自定义删除器释放 Person
析构 Person: Bob
 */
#include <iostream>
#include <memory>
#include <string>

class Person {
public:
    Person(const std::string& name) : name_(name) {
        std::cout << "构造 Person: " << name_ << "\n";
    }
    ~Person() {
        std::cout << "析构 Person: " << name_ << "\n";
    }
    void sayHello() const {
        std::cout << "Hello, I am " << name_ << "\n";
    }
private:
    std::string name_;
};

int main() {
    // 1. 创建 shared_ptr
    std::shared_ptr<Person> p1 = std::make_shared<Person>("Alice");
    std::cout << "p1.use_count = " << p1.use_count() << "\n";

    {
        // 2. 复制 shared_ptr(引用计数 +1)
        std::shared_ptr<Person> p2 = p1;
        // use_count() 用来返回当前共享同一对象的 shared_ptr 的数量.
        std::cout << "p1.use_count = " << p1.use_count() << "\n";
        std::cout << "p2.use_count = " << p2.use_count() << "\n";

        p2->sayHello();
    } // p2 离开作用域,引用计数 -1

    std::cout << "p1.use_count = " << p1.use_count() << "\n";

    // 3. 自定义删除器
    auto deleter = [](Person* p) {
        std::cout << "使用自定义删除器释放 Person\n";
        delete p;
    };

    std::shared_ptr<Person> p3(new Person("Bob"), deleter);
    std::cout << "p3.use_count = " << p3.use_count() << "\n";

    p3->sayHello();

    // 4. 使用 weak_ptr 观察 shared_ptr(不增加引用计数)
    std::weak_ptr<Person> wp = p3;
    std::cout << "p3.use_count = " << p3.use_count() << "\n";
    // expired() 用来判断 weak_ptr 所观察的对象是否已经被销毁.
    std::cout << "wp.expired = " << wp.expired() << "\n";

    // 5. 通过 weak_ptr 获取 shared_ptr
    if (auto sp = wp.lock()) {
        sp->sayHello();
        std::cout << "sp.use_count = " << sp.use_count() << "\n";
    }

    return 0;
}

指针与引用的区别

特性 指针 引用
声明 int* p; int& ref = a;
初始化 可不初始化,默认为野指针 必须初始化
使用时访问变量值 需要解引用 *p 直接使用 ref
重新赋值 指针可以指向不同地址 引用一旦绑定不可更改
空指针 可以指向空(nullptr 引用不能为“空”,必须绑定有效对象
  • 指针 指针是一个变量,存储的是另一个变量的内存地址。指针可以指向任何数据类型,包括基本类型、对象、函数等。实质是存储地址的变量,占用内存空间(通常是4或8字节,依赖于系统架构)。
  • 引用 引用是某个变量的别名,是已存在变量的一个别称。引用必须在定义时初始化,且一旦绑定到某个变量,就不能再绑定到其它变量。引用通常被实现为指针的常量别名,编译器在生成代码时会优化为直接访问目标变量,引用本身不占用额外空间。
+ 指针与引用代码示例
#include <iostream>

void incrementPointer(int* p) {
    if (p) {
        (*p)++;
    }
}

void incrementReference(int& r) {
    r++;
}

int main() {
    int a = 5;

    incrementPointer(&a);
    std::cout << "After incrementPointer: " << a << std::endl;  // 输出6

    incrementReference(a);
    std::cout << "After incrementReference: " << a << std::endl; // 输出7

    return 0;
}

函数指针

+ 函数指针代码示例

/**
 * 深入理解 int (*(*fp)(int (*)(int, int), int))(int, int)
1. 参数:
(*fp)(int (*)(int, int), int)
fp 是一个函数指针,指向的函数有两个参数:
第一个参数:int (*)(int, int), 一个函数指针,指向返回 int,参数为 (int, int) 的函数。
    例如:int add(int a, int b) 或 int mul(int a, int b)。
第二个参数:int.

2. 返回部分:
返回部分
(*fp)(...) 的返回值是:
int (*)(int, int)
即一个函数指针,指向返回 int,参数为 (int, int) 的函数。
换句话说,fp 所指向的函数会返回一个函数指针,这个函数指针本身可以再被调用。
 */

#include <stdio.h>

// 定义一个函数指针类型,指向返回 int,参数为 (int, int) 的函数
// 代表: int (*)(int, int)
typedef int (*func2_t)(int, int);

// 一个简单的函数,符合 func2_t 类型
int add(int a, int b) {
    return a + b;
}

int mul(int a, int b) {
    return a * b;
}

// 定义一个函数,符合 fp 的签名:
// 参数:一个 func2_t 和一个 int
// 返回:一个 func2_t
// 代表: (*fp)(int (*)(int, int), int)
func2_t chooser(func2_t f, int flag) {
    if (flag == 0) {
        return add;
    } else {
        return mul;
    }
}

int main() {
    /**
     * 声明 fp
     * 类似于:
     * typedef int (*func2_t)(int, int); // 二元函数指针类型
     * typedef func2_t (*fp_t)(func2_t, int); // 返回 func2_t,参数为 (func2_t, int) 的函数指针类型
     * fp_t fp; // fp 就是这种函数指针
    */
    int (*(*fp)(int (*)(int, int), int))(int, int);

    // 让 fp 指向 chooser
    fp = chooser;

    // 使用 fp:传入 add 和一个 flag,得到返回的函数指针
    func2_t f1 = fp(add, 0);  // 返回 add
    func2_t f2 = fp(add, 1);  // 返回 mul

    printf("f1(3,4) = %d\n", f1(3,4));  // 3+4 = 7
    printf("f2(3,4) = %d\n", f2(3,4));  // 3*4 = 12

    return 0;
}

/**
output:
f1(3,4) = 7
f2(3,4) = 12
 */

参考链接

  1. 透彻理解C++11 移动语义:右值、右值引用、std::move、std::forward
  2. 详解c++中的智能指针
  3. 智能指针与内存管理)
  4. 万字长文系统梳理一下C++函数指针
  5. C++ 函数指针 & 类成员函数指针
  6. C++指针难点梳理