Jeremy

LanguageENZH

C++ 手写代码实现

智能指针

唯一指针 unique_ptr

  • 唯一指针的特性:
    1. 唯一指针unique_ptr),顾名思义就是该指针是唯一的,不能分享(赋值)给其他指针,也不能讲其指赋予给其他唯一指针变量。
    2. 唯一指针只能被移动,意思是它只能从一个unique_ptr移动到另一个unique_ptr,一旦指针被移动到另一个唯一指针中,前一个的唯一指针就失去这个指针源,不再会指向对应的值。
    3. 可以使用std::move来移动唯一指针

  • 唯一指针的使用

唯一指针的方法在memory标准库中,使用时需要引入此lib

#include <iostream>
#include <memory>

class Test {
  private:
    int i, j, k;
  public:
    Test(int a, int b, int c): i(a), j(b), k(c) {}
    int sum() {
      return i + j + k;
    }
}

int main() {
  std::unique_ptr<Test> ptr1 = std::make_unique<Test>(1, 2, 3);

  std::cout << ptr1->sum() << std::endl; // 6

  std::unique_ptr<Test> ptr2 = std::move(ptr1);

  std::cout << ptr2->sum() << std::endl; // 6

  return 0;
}
  • 手写唯一指针

根据唯一指针的特性,可以自己手动实现其类和方法

#include <iostream>

template<typename T>
class MyUniquePtr {
  private:
    T *ptr;

  public:
    explicit MyUniquePtr(T *p = nullptr): ptr(p) {}

    ~MyUniquePtr() { delete ptr; }

    // 禁止拷贝
    MyUniquePtr(const MyUniquePtr &) = delete;
    MyUniquePtr &operator=(const MyUniquePtr &) = delete;

    // 支持移动
    MyUniquePtr(MyUniquePtr &&other) noexcept {
      ptr = other.ptr;
      other.ptr = nullptr;
    }
    MyUniquePtr &operator=(MyUniquePtr &&other) noexcept {
      if (this != &other) {
        delete ptr;
        ptr = other.ptr;
        other.ptr = nullptr;
      }

      return *this;
    }

    T *operator->() { return ptr; }
    T &opeartor*() { return *ptr; }
};

template<typename T, typename ...Args>
MyUniquePtr<T> my_make_unique(Args && ...args) {
  return MyUniquePtr<T>(new T(std::forward<Args>(args)...));
}

class Test {
  private:
    int i, j, k;
  public:
    Test(int a, int b, int c): i(a), j(b), k(c) {}
    int sum() {
      return i + j + k;
    }
};

int main() {

  MyUniquePtr<Test> ptr1 = my_make_unique<Test>(1, 2, 3);
  std::cout << ptr1->sum() << std::endl;

  MyUniquePtr<Test> ptr2 = std::move(ptr1);
  std::cout << ptr2->sum() << std::endl;

  return 0;
}

共享指针 (shared_ptr)

  • 共享指针特性
    1. 共享指针可以被复制。可以通过值传输其函数参数,然后分配到其他共享指针实例中。
    2. 每个共享指针都指向唯一的一个对象,并且共享每个“控制块”的访问权限。
    3. 当引用计数为0时,控制块会删除内存源和它本身

  • 共享指针使用

#include <iostream>
#include <memory>

int main() {
  std::shared_ptr<int> s1 = std::shared_ptr<int>(new int(10));

  std::shared_ptr<int> s2 = s1;

  std::cout << *s1 << std::endl; // 10
  std::cout << *s2 << std::endl; // 10
}

  • 手写共享指针
#include <iostream>

template <typename T> class MySharedPtr {
private:
  T *ptr;
  int *ref_counter;

public:
  MySharedPtr(T *p) : ptr(p) {
    if (p) {
      ref_counter = new int(1);
    } else {
      ref_counter = nullptr;
    }
  }

  // 拷贝构造
  MySharedPtr(const MySharedPtr &other) {
    ptr = other.ptr;
    ref_counter = other.ref_counter;
    (*ref_counter)++;
  }

  // 拷贝赋值
  MySharedPtr &operator=(const MySharedPtr &other) {
    if (this != &other) {
      release();

      ptr = other.ptr;
      ref_counter = other.ref_counter;
      (*ref_counter)++;
    }
    return *this;
  }

  ~MySharedPtr() { release(); }

  void release() {
    if (ref_counter) {
      (*ref_counter)--;

      if (*ref_counter == 0) {
        delete ptr;
        delete ref_counter;
      }
    }
  }

  T *operator->() { return ptr; }
  T &operator*() { return *ptr; }

  int use_count() const { return ref_counter ? *ref_counter : 0; }
};

class Test {
private:
  int i, j, k;

public:
  Test(int a, int b, int c) : i(a), j(b), k(c) {}
  int sum() { return i + j + k; }
};

int main() {
  MySharedPtr<Test> ptr1 = MySharedPtr<Test>(new Test(1, 2, 3));

  {
    MySharedPtr<Test> ptr2 = ptr1;
    std::cout << ptr1.use_count() << std::endl; // 2
  }

  std::cout << ptr1.use_count() << std::endl; // 1

  return 0;
}

弱引用 (weak_ptr)

弱引用是不会增加对象生命周期管理计数的引用,目的是避免指针的共享所有权

struct A;
struct B;

struct A {
  std::shared_ptr<B> b;
}

struct B {
  std::shared_ptr<A> a;
}

// 引用计数永远不为0, 会造成内存泄漏
  • 弱引用特性

    1. 不增加引用计数
    2. 不控制对象生命周期
    3. 可以“观察”对象是否还存在
  • 弱引用使用


int main() {
std::shared_ptr<int> sp = std::make_shared<int>(10);
std::weak_ptr<int> wp = sp;

if (!wp.expired()) {
  std::cout << "Object still alive" << std::cout;
}

if (auto sp2 = wp.lock()) {
  std::cout << *sp2 << std::endl;
} else {
  std::cout << "Object is already released." << std::endl;
}

}
  • 手写弱引用

手写若引用需要实现三部分:

  1. 控制块,包括shared_countweak_count 两个变量,当shared_count == 0 时, 对象销毁; 当shared_countweak_count 都为0时,控制块销毁
  2. SharePtr
  3. WeakPtr

其中 WeakPtr 需要实现:

  1. lock 方法:安全访问对象
  2. expired方法: 判断对象是否还存活
#include <iostream>

struct ControlBlock {
  int shared_count;
  int weak_count;
};

// 实现简易Share_Ptr
template <typename T> class MySharedPtr {

public:
  ControlBlock *cb;
  T *ptr;

  MySharedPtr(T *ptr) : ptr(ptr) {
    if (ptr) {
      cb = new ControlBlock{1, 0};
    } else {
      cb = nullptr;
    }
  }

  MySharedPtr(T *p, ControlBlock *c) {
    ptr = p;
    cb = c;
  }

  // 拷贝构造
  MySharedPtr(MySharedPtr &other) {
    ptr = other.ptr;
    cb = other.cb;
    if (cb) {
      cb->shared_count++;
    }
  }

  // 拷贝赋值
  MySharedPtr &operator=(const MySharedPtr &other) {
    if (this != &other) {
      release();

      ptr = other.ptr;
      cb->shared_count = other.cb->shared_count;
      (cb->shared_count)++;
    }
    return *this;
  }

  ~MySharedPtr() { release(); }

  void release() {
    if (!cb)
      return;

    cb->shared_count--;

    if (cb->shared_count == 0) {
      delete ptr;

      if (cb->weak_count == 0) {
        delete cb;
      }
    }
  }

  T *operator->() { return ptr; }
  T &operator*() { return *ptr; }
};

template <typename T> class WeakPtr {
public:
  T *ptr;
  ControlBlock *cb;

  WeakPtr() : ptr(nullptr), cb(nullptr) {}

  // 从 SharedPtr 构造
  WeakPtr(const MySharedPtr<T> &sp) {
    ptr = sp.ptr;
    cb = sp.cb;
    if (cb)
      cb->weak_count++;
  }

  // 拷贝构造
  WeakPtr(const WeakPtr &other) {
    ptr = other.ptr;
    cb = other.cb;
    if (cb)
      cb->weak_count++;
  }

  ~WeakPtr() {
    if (!cb)
      return;

    cb->weak_count--;

    if (cb->weak_count == 0 && cb->shared_count == 0) {
      delete cb;
    }
  }

  bool expired() const { return !cb || cb->shared_count == 0; }

  MySharedPtr<T> lock() const {
    if (expired()) {
      return MySharedPtr<T>();
    }

    cb->shared_count++;
    return MySharedPtr<T>(ptr, cb);
  }
};

int main() {
  MySharedPtr<int> mp1 = MySharedPtr<int>(new int(10)); // share_count = 1 weak_count = 0;
  MySharedPtr<int> mp2 = mp1;                           // share_count = 2 weak_count = 0;
  WeakPtr<int> wp1 = mp1;                               // share_count = 2 weak_count = 1;

  {
    WeakPtr<int> wp2 = wp1;                             // share_count = 2 weak_count = 2;
  }                                                     // share_count = 2 weak_count = 1;

  auto p3 = wp1.lock();                                 // share_count = 3 weak_count = 1;

  p3.~MySharedPtr();                                    // share_count = 2 weak_count = 1;

  p2.~MySharedPtr();                                    // share_count = 1 weak_count = 1;

  p1.~MySharedPtr();                                    // share_count = 0 weak_count = 1; Delete object

  wp1.~WeakPtr();                                       // share_count = 0 weak_count = 0; Delete control block
}

线程

通用线程池


#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
#include <utility>
#include <vector>

class ThreadPool {
private:
  std::vector<std::thread> pool;
  std::queue<std::function<void()>> queue;

  bool stop = false;
  std::mutex mtx;
  std::condition_variable cv;

private:
  // 每个线程的执行方法
  void worker_loop() {
    std::function<void()> task;
    {
      std::lock_guard<std::mutex> lock(mtx);
      std::cout << "Thread id: " << std::this_thread::get_id() << " is running"
                << std::endl;
    }

    while (true) {
      // 插入数据时添加唯一锁
      {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [this] { return stop || !queue.empty(); });
        if (stop && queue.empty()) {
          return;
        }
        // 取队首任务
        task = std::move(queue.front());
        // 弹出队首任务
        queue.pop();
      }
      try {
        task();
      } catch (std::exception &e) {
        std::cout << "Thread execute error: " << e.what() << std::endl;
      }
    }
  }

public:
  // 构造线程池
  ThreadPool(int thread_num) {
    for (int i = 0; i < thread_num; i++) {
      pool.emplace_back(&ThreadPool::worker_loop, this);
    }
  }

  // 析构函数
  ~ThreadPool() {
    {
      std::lock_guard<std::mutex> lock(mtx);
      stop = true;
    }
    // 通知所有线程往下执行
    cv.notify_all();

    for (auto &worker : pool) {
      if (worker.joinable()) {
        worker.join();
      }
    }
  }

  // 向线程池队列提交任务
  template <typename F, typename... Args>
  void submit(F &&func, Args &&...args) {
    std::lock_guard<std::mutex> lock(mtx);
    auto task = std::bind(std::forward<F>(func), std::forward<Args>(args)...);

    if (stop) {
      return;
    }

    queue.emplace(std::move(task));
    cv.notify_one();
  }
};

std::mutex cout_mtx;

// 创建测试任务
void delayedTask(int id, int delay_ms) {
  {
    std::lock_guard<std::mutex> lock(cout_mtx);
    std::cout << "[Task " << id << "] start on thread "
              << std::this_thread::get_id() << ", delay = " << delay_ms
              << "ms\n";
  }
  std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));

  {
    std::lock_guard<std::mutex> lock(cout_mtx);
    std::cout << "[Task " << id << "] end   on thread "
              << std::this_thread::get_id() << "\n";
  }
}

int main() {
  ThreadPool pool(5);

  pool.submit(delayedTask, 1, 1000);
  pool.submit(delayedTask, 2, 2000);
  pool.submit(delayedTask, 3, 3000);
  pool.submit(delayedTask, 4, 4000);
  pool.submit(delayedTask, 5, 5000);
  pool.submit(delayedTask, 6, 6000);

  return 0;

  /**
   * Possible output
   *  Thread id: 136194589456064 is running
      Thread id: 136194486695616 is running
      [Task 1] start on thread Thread id: 136194589456064, delay = 1000ms
      136194564277952 is running
      [Task 2] start on thread 136194564277952, delay = 2000ms
      Thread id: 136194581063360 is running
      [Task 3] start on thread 136194581063360, delay = 3000ms
      Thread id: [Task 136194572670656 is running
      4] start on thread 136194486695616, delay = 4000ms
      [Task 5] start on thread 136194572670656, delay = 5000ms
      [Task 1] end   on thread 136194589456064
      [Task 6] start on thread 136194589456064, delay = 6000ms
      [Task 2] end   on thread 136194564277952
      [Task 3] end   on thread 136194581063360
      [Task 4] end   on thread 136194486695616
      [Task 5] end   on thread 136194572670656
      [Task 6] end   on thread 136194589456064
   */
}