🎉 Welcome to Aerosand!.

05_vector

0. 前言

上一篇浏览了几个常见的基础类,本文尝试讨论 vector 类的部分细节,帮助读者熟悉开发流程、工具使用、编译原理。

本文主要讨论

  • 练习源码的查阅
  • 讨论 vector 类的部分代码实现
  • 理解原生库的使用
  • 编译运行 vector 项目

1. Vector 类

API 页面 https://api.openfoam.com/2506/classFoam_1_1Vector.html

终端输入命令,本地查找

terminal
find $FOAM_SRC -iname vector

终端输入命令,打开该类的文件夹

terminal
code $FOAM_SRC/OpenFOAM/primitives/Vector

该类的文件结构如下

terminal
tree -L 1
.
├── bools/
├── complex/
├── floats/
├── ints/
├── lists/
├── Vector.H
└── VectorI.H

阅读 Vector.H 的代码描述可知,该类是 3D Vector 的模板类,继承自 VectorSpace 类并添加封装了 3 分量的构造、分量元素的接口函数、点积和叉积的等方法。

具体到 Vector.H 的代码,简单方法在代码中直接实现,复杂方法则大量使用内联函数(inline function)以提高代码性能。OpenFOAM 特别提供 VectorI.H 文件来写内联函数的实现。

进入 Vector/int 文件夹,查看 Vector/ints/labelVector.H 代码

...
typedef Vector<label> labelVector;
...

进入 Vector/floats 文件夹,查看 Vector/floats/vector.H 代码

Vector/floats/vector.H
...
//! \class Foam::floatVector
//! \brief A Vector of values with float precision
typedef Vector<float> floatVector;

//! \class Foam::doubleVector
//! \brief A Vector of values with double precision
typedef Vector<double> doubleVector;

// With float or double precision (depending on compilation)
typedef Vector<scalar> vector;

// With float or double precision (depending on compilation)
typedef Vector<solveScalar> solveVector;
...

可以看到,代表不同数据类型的文件夹 /bools/complex/floats/ints/lists 都是 Vector 模板类对不同基本数据类型的 typedef ,也就是类型别名。

Warning

暂不深究代码的所有细节

2. 源码讨论

2.1. 声明

查阅 Vector/Vector.H 代码,我们分段讨论

API 页面 https://api.openfoam.com/2506/Vector_8H_source.html

Tip

API 的代码页可以点击跳转到不同的头文件、类、函数等。

Vector/Vector.H
#ifndef Foam_Vector_H
#define Foam_Vector_H
// 预处理指令,防止头文件被重复包含

#include "contiguous.H" // 提供类型连续性检查的功能
#include "VectorSpace.H" // 基类

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

namespace Foam // 命名空间
{

// Forward Declarations
template<class T> class List; // 前向声明
// 声明了 List 模板类,后续可能会用到,但暂时不用提供完整头文件

/*---------------------------------------------------------------------------*\
                           Class Vector Declaration
\*---------------------------------------------------------------------------*/

template<class Cmpt>
class Vector // 定义 Vector 模板类
:
    public VectorSpace<Vector<Cmpt>, Cmpt, 3> // 继承自基类,3维向量空间
{
public:

    // Typedefs // 定义类型别名

        //- Equivalent type of labels used for valid component indexing
        typedef Vector<label> labelType;
        // 使用 label 类型作为分量


    // Member Constants

        //- Rank of Vector is 1
        static constexpr direction rank = 1; // 定义秩为一阶


    //- Component labeling enumeration
    enum components { X, Y, Z }; // 枚举对应向量的分量


    // Generated Methods

        //- Default construct
        Vector() = default; // 默认构造

        //- Copy construct
        Vector(const Vector&) = default; // 拷贝构造

        //- Copy assignment
        Vector& operator=(const Vector&) = default; // 拷贝赋值


    // Constructors

        //- Construct initialized to zero
        inline Vector(const Foam::zero); // 置零构造

        //- Copy construct from VectorSpace of the same rank
        template<class Cmpt2>
        inline Vector(const VectorSpace<Vector<Cmpt2>, Cmpt2, 3>& vs);
        // 从另一种分量类型的向量空间构造向量

        //- Construct from three components
        inline Vector(const Cmpt& vx, const Cmpt& vy, const Cmpt& vz);
        // 从三个分量值直接构造向量

        //- Construct from Istream
        inline explicit Vector(Istream& is);
        // 从输入流读取并构造向量
        // explicit 关键字防止隐式转换


    // Member Functions

    // Component Access

        //- Access to the vector x component
        const Cmpt& x() const noexcept { return this->v_[X]; }

        //- Access to the vector y component
        const Cmpt& y() const noexcept { return this->v_[Y]; }

        //- Access to the vector z component
        const Cmpt& z() const noexcept { return this->v_[Z]; }
        
        // 使用枚举值 X, Y, Z 作为索引访问基类的 v_ 数组
        // noexcept 表示这些函数不会抛出异常

        //- Access to the vector x component
        Cmpt& x() noexcept { return this->v_[X]; }

        //- Access to the vector y component
        Cmpt& y() noexcept { return this->v_[Y]; }

        //- Access to the vector z component
        Cmpt& z() noexcept { return this->v_[Z]; }
        
        // 声明非常量成员函数,用于访问和修改向量的分量


    // Vector Operations

        //- Return \c this (for point which is a typedef to Vector\<scalar\>)
        inline const Vector<Cmpt>& centre
        (
            const Foam::UList<Vector<Cmpt>>&  /* (unused) */
        ) const noexcept;
        // 定义在内联函数中
        // 返回向量本身(对于点类型很有用)
        // 参数未使用,但为了接口一致性而保留

        //- The length (L2-norm) of the vector
        inline scalar mag() const;
        // 定义在内联函数中
        // 返回向量的模(长度)

        //- The length (L2-norm) squared of the vector.
        inline scalar magSqr() const;
        // 定义在内联函数中
        // 返回向量模的平方(避免开方运算,提高性能)

        //- The L2-norm distance from another vector.
        //- The mag() of the difference.
        inline scalar dist(const Vector<Cmpt>& v2) const;
        // 定义在内联函数中
        // 返回与另一个向量的距离

        //- The L2-norm distance squared from another vector.
        //- The magSqr() of the difference.
        inline scalar distSqr(const Vector<Cmpt>& v2) const;
        // 定义在内联函数中
        // 返回与另一个向量距离的平方

        //- Inplace normalise the vector by its magnitude
        //  For small magnitudes (less than ROOTVSMALL) set to zero.
        //  Will not be particularly useful for a vector of labels
        inline Vector<Cmpt>& normalise(const scalar tol = ROOTVSMALL);
        // 定义在内联函数中
        // 将向量归一化(转换为单位向量)
        // tol 参数是一个容差值,用于处理非常小的向量

        //- Inplace removal of components that are collinear to the given
        //- unit vector.
        inline Vector<Cmpt>& removeCollinear(const Vector<Cmpt>& unitVec);
        // 定义在内联函数中
        // 移除与给定单位向量共线的分量

        //- Scalar-product of \c this with another Vector.
        inline Cmpt inner(const Vector<Cmpt>& v2) const;
        // 定义在内联函数中
        // 返回与另一个向量的点积(内积)

        //- Cross-product of \c this with another Vector.
        inline Vector<Cmpt> cross(const Vector<Cmpt>& v2) const;
        // 定义在内联函数中
        // 返回与另一个向量的叉积(外积)


    // Comparison Operations

        //- Lexicographically compare \em a and \em b with order (x:y:z)
        static inline bool less_xyz
        (
            const Vector<Cmpt>& a,
            const Vector<Cmpt>& b
        );
        // 定义在内联函数中
        // 按 X→Y→Z 的字典序比较两个向量

        //- Lexicographically compare \em a and \em b with order (y:z:x)
        static inline bool less_yzx
        (
            const Vector<Cmpt>& a,
            const Vector<Cmpt>& b
        );
        // 定义在内联函数中
        // 按 Y→Z→X 的字典序比较两个向量

        //- Lexicographically compare \em a and \em b with order (z:x:y)
        static inline bool less_zxy
        (
            const Vector<Cmpt>& a,
            const Vector<Cmpt>& b
        );
        // 定义在内联函数中
        // 按 Z→X→Y 的字典序比较两个向量
};


// * * * * * * * * * * * * * * * * * Traits  * * * * * * * * * * * * * * * * //

// 类型特性,暂不深究

//- Data are contiguous if component type is contiguous
template<class Cmpt>
struct is_contiguous<Vector<Cmpt>> : is_contiguous<Cmpt> {};
// 如果分量类型 Cmpt 是连续的,则 Vector<Cmpt> 也是连续的

//- Data are contiguous label if component type is label
template<class Cmpt>
struct is_contiguous_label<Vector<Cmpt>> : is_contiguous_label<Cmpt> {};
// 如果分量类型 Cmpt 是 label 类型且连续,则 Vector<Cmpt> 也是

//- Data are contiguous scalar if component type is scalar
template<class Cmpt>
struct is_contiguous_scalar<Vector<Cmpt>> : is_contiguous_scalar<Cmpt> {};
// 如果分量类型 Cmpt 是 scalar 类型且连续,则 Vector<Cmpt> 也是


template<class Cmpt>
class typeOfRank<Cmpt, 1>
{
public:

    typedef Vector<Cmpt> type;
};


template<class Cmpt>
class symmTypeOfRank<Cmpt, 1>
{
public:

    typedef Vector<Cmpt> type;
};


template<class Cmpt>
class typeOfSolve<Vector<Cmpt>>
{
public:

    typedef Vector<solveScalar> type;
};

// 暂不深究

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

} // End namespace Foam

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

#include "VectorI.H"

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

#endif

2.2. 内联

很多方法的定义在内联文件中

API 页面 https://api.openfoam.com/2506/VectorI_8H_source.html

查阅 Vector/VectorI.H 代码

Vector/VectorI.H
// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 template<class Cmpt>
 inline Foam::Vector<Cmpt>::Vector(const Foam::zero)
 :
     Vector::vsType(Zero)
 {}
 // 置零构造
 // - 传入 Zero 常量
 
 
 template<class Cmpt>
 template<class Cmpt2>
 inline Foam::Vector<Cmpt>::Vector
 (
     const VectorSpace<Vector<Cmpt2>, Cmpt2, 3>& vs
 )
 :
     Vector::vsType(vs)
 {}
 // 模板构造函数
 // - 从不同分量类型的向量构造
 
 
 template<class Cmpt>
 inline Foam::Vector<Cmpt>::Vector
 (
     const Cmpt& vx,
     const Cmpt& vy,
     const Cmpt& vz
 )
 {
     this->v_[X] = vx;
     this->v_[Y] = vy;
     this->v_[Z] = vz;
 }
 // 从三个分量构造
 // - 直接访问继承自 VectorSpace 的 v_ 数组成员 
 
 template<class Cmpt>
 inline Foam::Vector<Cmpt>::Vector(Istream& is)
 :
     Vector::vsType(is)
 {}
 // 从输入流构造向量
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 template<class Cmpt>
 inline const Foam::Vector<Cmpt>& Foam::Vector<Cmpt>::centre
 (
     const Foam::UList<Vector<Cmpt>>&
 ) const noexcept
 {
     return *this;
 }
 // 直接返回当前对象的引用(*this)
 
 
 template<class Cmpt>
 inline Foam::scalar Foam::Vector<Cmpt>::magSqr() const
 {
     return
     (
         Foam::magSqr(this->x())
       + Foam::magSqr(this->y())
       + Foam::magSqr(this->z())
     );
 }
 // 计算向量模的平方
 // - 使用 OpenFOAM 的 magSqr() 函数计算各分量的平方
 
 
 template<class Cmpt>
 inline Foam::scalar Foam::Vector<Cmpt>::mag() const
 {
     return ::sqrt(this->magSqr());
 }
 // 计算向量模
 // - 先计算模的平方,然后调用标准库的 sqrt 函数开方
 
 
 template<class Cmpt>
 inline Foam::scalar Foam::Vector<Cmpt>::distSqr(const Vector<Cmpt>& v2) const
 {
     return
     (
         Foam::magSqr(v2.x() - this->x())
       + Foam::magSqr(v2.y() - this->y())
       + Foam::magSqr(v2.z() - this->z())
     );
 }
 // 计算与另一向量距离的平方
 // - 计算各分量差值的平方和
 
 
 template<class Cmpt>
 inline Foam::scalar Foam::Vector<Cmpt>::dist(const Vector<Cmpt>& v2) const
 {
     return ::sqrt(this->distSqr(v2));
 }
 // 计算与另一向量距离
 // - 先计算距离平方,然后开方
 
 
 template<class Cmpt>
 inline Foam::Vector<Cmpt>& Foam::Vector<Cmpt>::normalise(const scalar tol)
 {
     #ifdef __clang__
     volatile  // Use volatile to avoid aggressive branch optimization
     #endif
     const scalar s = this->mag();
 
     if (s < tol)
     {
         *this = Zero;
     }
     else
     {
         *this /= s;
     }
 
     return *this;
 }
 // 向量归一化
 // - 使用 #ifdef __clang__ 和 volatile 关键字防止 Clang 编译器进行过于激进的优化
 // - 如果模长小于容差值 tol,则将向量设为零向量
 // - 否则,将向量除以其模长,使其成为单位向量
 // - 返回当前对象的引用
 
 
 template<class Cmpt>
 inline Foam::Vector<Cmpt>&
 Foam::Vector<Cmpt>::removeCollinear(const Vector<Cmpt>& unitVec)
 {
     *this -= (*this & unitVec) * unitVec;
     return *this;
 }
 // 移除与给定单位向量共线分量
 // - 计算当前向量在给定单位向量方向上的投影
 // - 从当前向量中减去这个投影分量
 // - 返回当前对象的引用
 
 
 // * * * * * * * * * * * * * * * Member Operations * * * * * * * * * * * * * //
 
 template<class Cmpt>
 inline Cmpt Foam::Vector<Cmpt>::inner(const Vector<Cmpt>& v2) const
 {
     const Vector<Cmpt>& v1 = *this;
 
     return (v1.x()*v2.x() + v1.y()*v2.y() + v1.z()*v2.z());
 }
 // 计算点积(内积)
 // - 计算两个向量各分量乘积的和
 
 
 template<class Cmpt>
 inline Foam::Vector<Cmpt>
 Foam::Vector<Cmpt>::cross(const Vector<Cmpt>& v2) const
 {
     const Vector<Cmpt>& v1 = *this;
 
     return Vector<Cmpt>
     (
         (v1.y()*v2.z() - v1.z()*v2.y()),
         (v1.z()*v2.x() - v1.x()*v2.z()),
         (v1.x()*v2.y() - v1.y()*v2.x())
     );
 }
 // 计算叉积(外积)
 // - 按照叉积公式计算新向量的各分量
 // - 返回一个新的 Vector 对象
 
 
 // * * * * * * * * * * * * * Comparison Operations * * * * * * * * * * * * * //
 
 template<class Cmpt>
 inline bool
 Foam::Vector<Cmpt>::less_xyz(const Vector<Cmpt>& a, const Vector<Cmpt>& b)
 {
     return
     (
         (a.x() < b.x())  // Component is less
      ||
         (
             !(b.x() < a.x())  // Equal? Check next component
          &&
             (
                 (a.y() < b.y())  // Component is less
              ||
                 (
                     !(b.y() < a.y())  // Equal? Check next component
                  && (a.z() < b.z())
                 )
             )
         )
     );
 }
 // 按 X→Y→Z 字典序比较两个向量
 // - 先比较 X 分量,如果不等则返回结果
 // - 如果 X 分量相等,则比较 Y 分量
 // - 如果 Y 分量也相等,则比较 Z 分量
 
 
 template<class Cmpt>
 inline bool
 Foam::Vector<Cmpt>::less_yzx(const Vector<Cmpt>& a, const Vector<Cmpt>& b)
 {
     return
     (
         (a.y() < b.y())  // Component is less
      ||
         (
             !(b.y() < a.y())  // Equal? Check next component
          &&
             (
                 (a.z() < b.z())  // Component is less
              ||
                 (
                     !(b.z() < a.z())  // Equal? Check next component
                  && (a.x() < b.x())
                 )
             )
         )
     );
 }
 // 按 Y→Z→X 字典序比较两个向量
 
 
 template<class Cmpt>
 inline bool
 Foam::Vector<Cmpt>::less_zxy(const Vector<Cmpt>& a, const Vector<Cmpt>& b)
 {
     return
     (
         (a.z() < b.z())  // Component is less
      ||
         (
             !(b.z() < a.z())  // Equal? Check next component
          &&
             (
                 (a.x() < b.x())  // Component is less
              ||
                 (
                     !(b.x() < a.x())  // Equal? Check next component
                  && (a.y() < b.y())
                 )
             )
         )
     );
 }
 // 按 Z→X→Y 字典序比较两个向量
 
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
 {
 
 // * * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * //
 
 //- Linear interpolation of vectors a and b by factor t
 template<class Cmpt>
 inline Vector<Cmpt> lerp
 (
     const Vector<Cmpt>& a,
     const Vector<Cmpt>& b,
     const scalar t
 )
 {
     const scalar onet = (1-t);
 
     return Vector<Cmpt>
     (
         onet*a.x() + t*b.x(),
         onet*a.y() + t*b.y(),
         onet*a.z() + t*b.z()
     );
 }
 // 线性插值函数
 // - 计算两个向量的线性插值结果
 // - t 是插值参数,取值范围 [0, 1]
 // - 当 t=0 时返回 a,t=1 时返回 b,中间值则返回两者的线性组合
 
 
 // * * * * * * * * * * * * * * * Global Operators  * * * * * * * * * * * * * //
 
 //- Dummy innerProduct for scalar
 //  Allows the construction of vtables for virtual member functions
 //  involving the inner-products of fields
 //  for which a "NotImplemented" specialization for scalar is provided.
 template<class Cmpt>
 class innerProduct<Vector<Cmpt>, scalar>
 {
 public:
 
     typedef scalar type;
 };
 // 为 Vector<Cmpt> 和 scalar 的特化内积类型特征
 // - 定义内积结果为 scalar 类型
 
 
 template<class Cmpt>
 inline Cmpt operator&(const Vector<Cmpt>& v1, const Vector<Cmpt>& v2)
 {
     return v1.inner(v2);
 }
 // 点积运算符的重载
 // - 调用 inner 成员函数计算点积
 
 
 template<class Cmpt>
 inline Vector<Cmpt> operator^(const Vector<Cmpt>& v1, const Vector<Cmpt>& v2)
 {
     return v1.cross(v2);
 }
 // 叉积运算符的重载
 // - 调用 cross 成员函数计算叉积
 
 
 template<class Cmpt>
 inline Vector<Cmpt> operator*(const Cmpt& s, const Vector<Cmpt>& v)
 {
     return Vector<Cmpt>(s*v.x(), s*v.y(), s*v.z());
 }
 // 标量与向量乘法运算符的重载(标量在前)
 // - 将向量的每个分量乘以标量
 // - 返回新的向量
 
 template<class Cmpt>
 inline Vector<Cmpt> operator*(const Vector<Cmpt>& v, const Cmpt& s)
 {
     return s*v;
 }
 // 向量与标量乘法运算符的重载(标量在后)
 // - 通过调用前面的实现,确保乘法可交换
 
 
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace Foam

Warning

上面这些代码讨论主要是为了帮助读者熟悉 C++ 语言在 OpenFOAM 中的使用,克服对 C++ 语言的陌生和恐惧,便于读者理解后续实践的代码。暂时不需要花费更多时间去阅读更多 OpenFOAM 的源代码,也不需要深究代码细节,后续会在 ofsc 系列讨论代码。

3. 项目构建

终端输入命令,建立本文项目

terminal
ofsp
mkdir ofsp_06_vector
code ofsp_06_vector

继续使用终端命令或者使用 vscode 界面创建其他文件,最终文件结构如下

terminal
tree
.
├── Aerosand
│   ├── Aerosand.C
│   ├── Aerosand.H
│   └── Make
│       ├── files
│       └── options
├── Make
│   ├── files
│   └── options
└── ofsp_06_vector.C

4. 开发库

4.1. 库源码

代码 Aerosand.H

/Aerosand/Aerosand.H
#pragma once

class Aerosand
{
public:
    void SetLocalTime(double t);
    double GetLocalTime() const;


private:
    double localTime_;
};

代码 Aerosand.C

/Aerosand/Aerosand.C
#include "Aerosand.H"

void Aerosand::SetLocalTime(double t) {
    localTime_ = t;
}

double Aerosand::GetLocalTime() const {
    return localTime_;
}

4.2. 库 Make

Make/files

/Aerosand/Make/files
Aerosand.C

LIB = $(FOAM_USER_LIBBIN)/libAerosand

开发库没有其他依赖,库 Make/options 为空

4.3. 库编译

终端输入命令,进行库的编译

terminal
wclean Aerosand
wmake Aerosand

5. 主项目

5.1. 主源码

代码 ofsp_06_vector.C

/ofsp_06_vector.C
#include <iostream>

#include "Aerosand.H"

#include "vector.H"

using namespace Foam;

int main()
{
    scalar s(3.14);
    vector v1(1, 2, 3);
    vector v2(2, 3, 4);
    Info<< s << " * " << v1 << " = "  << s * v1 << nl 
        << "distance:  " << v1.dist(v2) << nl
        << "normalise: " << v1.normalise(1e-6) << nl
        << "inner product: " << v1.inner(v2) << nl
        << "inner product: " << (v1 & v2) << nl // 使用括号提高计算优先级
        << "cross product: " << v1.cross(v2) << nl
        << "cross product: " << (v1^v2) << nl // 使用括号提高计算优先级
        << endl;

	label a = 1;
    scalar pi = 3.1415926;
    Info<< "Hi, OpenFOAM!" << " Here we are." << nl
	    << a << " + " << pi << " = " << a + pi << nl
	    << a << " * " << pi << " = " << a * pi << nl
	    << endl;


    Aerosand mySolver;
    mySolver.SetLocalTime(0.2);
    Info<< "\nCurrent time step is : " << mySolver.GetLocalTime() << endl;

    return 0;
}

5.2. 项目 Make

项目 Make/files

/Make/files
ofsp_06_vector.C

EXE = $(FOAM_USER_APPBIN)/ofsp_06_vector

因为我们包含了 vector.H ,路径为 $FOAM_SRC/OpenFOAM/primitives/Vector/floats/vector.H。由 Make 文件的位置能判断,该文件属于 OpenFOAM 库,路径为 $FOAM_SRC/OpenFOAM。归属关系如下。

terminal
$FOAM_SRC/OpenFOAM
├── lnInclude/
├── ...
├── Make
│   ├── files
│   └── options
└── primitives
	├── ...
	└── Vector
		├── ...
		├── floats
		│   ├── ...
		│   └── vector.H
		├── Vector.H
		└── VectorI.H

原则上,我们应该在项目 Make/options 中包含 OpenFOAM 库。实际上,OpenFOAM 的构建系统(wmake)已经自动处理原生必备库的依赖关系。我们只需要添加第三方库、自己开发的库、某些可选模块的库即可。

Important

$FOAM_SRC/OpenFOAM 库已经自动依赖,其中类的使用均无需用户再次链接。

项目 Make/options

/Make/options
EXE_INC = \
    -IAerosand/lnInclude

EXE_LIBS = \
    -L$(FOAM_USER_LIBBIN) \
    -lAerosand

6. 编译运行

终端输入命令,编译运行该项目

terminal
wclean
wmake
ofsp_06_vector

运行结果如下

terminal
3.14 * (1 2 3) = (3.14 6.28 9.42)
distance:  1.73205
normalise: (0.267261 0.534522 0.801784)
inner product: 5.34522
inner product: 5.34522
cross product: (-0.267261 0.534522 -0.267261)
cross product: (-0.267261 0.534522 -0.267261)

Hi, OpenFOAM! Here we are.
1 + 3.14159 = 4.14159
1 * 3.14159 = 3.14159


Current time step is : 0.2

7. 小结

本文完成讨论

  • 练习源码的查阅
  • 讨论 vector 类的部分代码实现
  • 理解原生库的使用
  • 编译运行 vector 项目
Last updated on