Skip to content

模板

以往,我们在编写 C++ 代码的时候,都会把变量类型、函数参数类型等指定为具体某一种类型。但假设现在我们有一个函数,它支持多种类型,我们有什么方法可以实现呢?可能有人会想可以通过函数重载来实现,但这会导致许多重复代码。这里我要引出一个新概念,泛型编程 —— 把类型参数化,在实例化时让用户指定类型。在 C++ 中,我们可以通过模板来实现泛型编程。

cpp
/**
 * Copyright 2023 <Hardworking Bee>
 */
#include <iostream>

using std::cout;

// 约定俗成,模板类型参数通常用单个大写字母表示。
template <typename T>
T add(T a, T b) {
  return a + b;
}

int main() {
  // 编译器会根据参数类型自动推导出模板参数类型。
  cout << add(1, 2) << '\n';
  cout << add(1.1, 2.2) << '\n';

  // 也可以显式指定模板参数类型。
  cout << add<int>(1, 2) << '\n';
  cout << add<double>(1.1, 2.2) << '\n';

  return 0;
}

调用函数时,编译器会根据模板参数类型生成对应类型的函数,这个过程称作“模板实例化”。你可以在调试模式运行以上代码,然后打开反汇编视图,可以发现上述两次 add 的调用是两个不同的函数,分别对应 intdouble 类型。

类模板

C++ 中除了函数模板外,我们还可以编写类模板。

cpp
/**
 * Copyright 2023 <Hardworking Bee>
 */

#include <iostream>

using std::cout;

template <typename T>
class Array {
  template <typename T>
  friend std::ostream& operator<<(std::ostream&, const Array<T>&);

 private:
  T* data_;
  // 元素个数
  int size_{0};
  // 容量
  int capacity_;

 public:
  explicit Array(int capacity = 0) {
    capacity_ = capacity > 0 ? capacity : 1;
    data_ = new T[capacity_];
  }

  ~Array() {
    if (data_ == nullptr) return;
    delete[] data_;
  }

  T& operator[](int index) {
    if (index < 0 || index >= capacity_)
      throw std::out_of_range("index out of range");
    return data_[index];
  }

  int size() const { return size_; }

  int capacity() const { return capacity_; }

  T& Push(T value) {
    if (size_ < capacity_) {
      data_[size_++] = value;
      return data_[size_ - 1];
    }

    int* new_data = new T[capacity_ + 1];
    for (int i = 0; i < capacity_; ++i) {
      new_data[i] = data_[i];
    }
    new_data[capacity_] = value;
    delete[] data_;
    data_ = new_data;
    ++size_;
    ++capacity_;
    return data_[capacity_ - 1];
  }
};

template <typename T>
std::ostream& operator<<(std::ostream& os, const Array<T>& arr) {
  os << '[';
  int last_idx = arr.size() - 1;
  for (int i = 0; i < last_idx; ++i) {
    os << arr.data_[i] << ", ";
  }
  os << arr.data_[last_idx] << ']';
  return os;
}

int main() {
  Array<int> arr(10);
  for (int i = 0; i < 10; ++i) {
    arr.Push(i);
  }
  for (int i = 0; i < 10; ++i) {
    cout << arr[i] << '\n';
  }
  cout << arr << '\n';
  return 0;
}

References