30、C++ 实用工具、容器扩展与标准库实现

C++ 实用工具、容器扩展与标准库实现

1. Boost 中的循环缓冲区类

在 C++ 编程中,Boost 提供了一个经过优化的循环缓冲区类( circular_buffer ),它支持迭代器且符合 STL 标准。使用这个类非常简单,以下是一个示例代码:

typedef circular_buffer<std::uint8_t, 4U> buffer_type;
void do_something()
{
    buffer_type buffer;
    // Put three bytes into the buffer.
    buffer.in(1U);
    buffer.in(2U);
    buffer.in(3U);
    // The size of the buffer is 3.
    const buffer_type::size_type count = buffer.size();
    // The buffer is not empty.
    const bool is_empty = buffer.empty();
    // Extract the first element.
    const buffer_type::value_type value = buffer.out();
    // The size of the buffer is now 2.
    count = buffer.size();
}

上述代码展示了如何使用 circular_buffer 类进行数据的插入和提取操作。

2. Boost 库概述

Boost 库是一个庞大的通用工具集合,面向广泛的 C++ 用户和应用领域。它扩展了 C++ 语言规范之外的功能,包含许多独立的库,如通用工具库、数值和词法操作库、数学和数值库、线程和并发库、图像处理库、网络库、任务调度库、正则表达式库等。Boost 库以其高质量而闻名,部分原因是候选库在被纳入 Boost 之前要经过同行评审。

3. 自定义动态数组容器 dynamic_array

3.1 定义 dynamic_array 容器

在 C++ 中, std::array std::vector 是常用的容器,但它们各有优缺点。 std::array 效率高,但在编译时大小固定; std::vector 支持运行时动态调整大小,但由于其动态分配机制,存在轻微的性能和存储劣势。

为了结合两者的优点,我们可以创建一个自定义的 dynamic_array 容器。它可以在构造时动态分配一次大小,之后在对象的整个生命周期内保持大小不变。以下是使用示例:

// A dynamic array of four counters initialized with 1.
dynamic_array<unsigned> counters(4U, 1U);
void do_something()
{
    // Increment the counters.
    std::for_each(std::begin(counters),
                  std::end(counters),
                  [](unsigned& u)
                  {
                      ++u;
                  });
    // It is not possible to resize the dynamic_array.
}

dynamic_array 是一种混合容器,结合了数组的效率和 std::vector 的动态大小调整(尽管是一次性分配)功能。它旨在满足顺序 STL 容器的一般要求,与 STL 基本一致,填补了固定大小的 std::array 和动态的 std::vector 之间的功能空白。

3.2 实现和使用 dynamic_array

以下是 dynamic_array 类的可能实现:

#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <memory>
template<typename T,
         typename alloc = std::allocator<T>>
class dynamic_array
{
public:
    // Type definitions.
    typedef alloc allocator_type;
    typedef T value_type;
    typedef T& reference;
    typedef const T& const_reference;
    typedef T* iterator;
    typedef const T* const_iterator;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;
    typedef std::reverse_iterator<iterator> reverse_iterator;
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
    // Constructors.
    dynamic_array();
    dynamic_array(size_type);
    dynamic_array(
        size_type,
        const value_type&,
        const allocator_type& = allocator_type());
    dynamic_array(const dynamic_array&);
    template<typename input_iterator>
    dynamic_array(
        input_iterator,
        input_iterator,
        const allocator_type& = allocator_type());
    dynamic_array(
        std::initializer_list<T> lst,
        const allocator_type& = allocator_type());
    // Destructor.
    ~dynamic_array();
    // Iterator members:
    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;
    const_iterator cbegin () const;
    const_iterator cend () const;
    reverse_iterator rbegin ();
    reverse_iterator rend ();
    const_reverse_iterator rbegin () const;
    const_reverse_iterator rend () const;
    const_reverse_iterator crbegin() const;
    const_reverse_iterator crend () const;
    // Size and capacity.
    size_type size() const;
    size_type max_size() const;
    bool empty() const;
    // Element access members.
    reference operator[](const size_type);
    const_reference operator[](const size_type);
    reference front();
    const_reference front() const;
    reference back ();
    const_reference back () const;
    reference at(const size_type);
    const_reference at(const size_type) const;
    // Element manipulation members.
    void fill(const value_type&);
    void swap(dynamic_array&);
private:
    const size_type N;
    pointer elems;
    // Note: dynamic_array can not be copied
    // with operator=().
    dynamic_array& operator=(const dynamic_array&);
};

以其中一个构造函数为例,其实现如下:

dynamic_array(size_type count,
              const value_type& v,
              const allocator_type& a) : N(count)
{
    const size_type the_size =
        std::max(size_type(1U), N);
    elems = allocator_type(a).allocate(the_size);
    if(N > size_type(0U))
    {
        std::fill_n(begin(), N, v);
    }
    else
    {
        elems[0U] = value_type();
    }
}

dynamic_array 容器满足顺序 STL 容器的大部分一般要求,因此可以与 STL 的标准算法一起使用。例如,以下代码初始化一个 dynamic_array 并计算其字节校验和:

util::dynamic_array<int> values ( { 1, 2, 3 } );
int sum = std::accumulate(values.begin(),
                          values.end(),
                          0);

此外, dynamic_array 还可以与其他函数和类类型一起使用,例如在通信类中:

class communication
{
public:
    communication() { }
    ~communication() { }
    bool send(const dynamic_array<std::uint8_t>& cmd);
    bool recv(dynamic_array<std::uint8_t>& rsp);
};

3.3 不同容器特性对比

容器类型 编译时大小 运行时调整大小 性能和存储
std::array 固定 不支持 效率高
std::vector 不固定 支持 有轻微劣势
dynamic_array 不固定(构造时确定) 不支持 结合两者优点

3.4 dynamic_array 使用流程

graph TD;
    A[定义 dynamic_array 类型] --> B[创建对象并初始化];
    B --> C[使用 STL 算法操作];
    C --> D[使用对象进行其他操作];

4. 手动实现 C++ 标准库部分组件

4.1 背景

有些 C++ 编译器可能不提供完整的 C++ 标准库和 STL 实现,或者提供的实现不完整或过时。在这种情况下,如果开发和测试能够满足实时 C++ 的可靠性要求,我们可以手动编写部分组件。

4.2 实现策略

4.2.1 选择存储位置

选择一个单一的位置来存储库头文件和必要的源文件,例如一个根目录结合特定平台库部分的子目录。这样可以简化将路径信息添加到编译器默认搜索路径的过程。

4.2.2 选择实现组件

选择实现的 C++ 库组件主要基于实用性和实现难度。以下是一些建议实现的组件:
- 固定大小的整数类型,包括具有精确位数的类型、至少具有特定位数的类型和至少具有一定位数的最快类型。
- 部分支持 std::array ,可选择不包括反向迭代器。
- <algorithm> 库中常用且易于实现的函数,如 std::min() std::max() std::for_each() std::fill() std::copy() std::find_if() 等。
- <type_traits> 库的选定部分,特别是 std::enable_if 和用于检查类型的各种模板,如 std::is_integral
- 如果项目中需要进行浮点数基本函数计算,可实现 <cmath> 中的常用数学函数。

4.3 部分组件的实现示例

4.3.1 固定大小整数类型

如果编译器具有 C99 兼容性并支持 C99 固定大小整数类型,可以将这些类型注入到 std 命名空间:

// A partial implementation of <cstdint>
// Include the C99 fixed-size integers.
#include <stdint.h>
namespace std
{
    // Types with an exact number of bits.
    using ::uint8_t;
    using ::uint16_t;
    using ::uint32_t;
    using ::uint64_t;
    // Types with at least a certain number of bits.
    using ::uint_least8_t;
    using ::uint_least16_t;
    using ::uint_least32_t;
    using ::uint_least64_t;
    // Fastest types with at least a certain
    // number of bits.
    using ::uint_fast8_t;
    using ::uint_fast16_t;
    using ::uint_fast32_t;
    using ::uint_fast64_t;
}

如果编译器不具有 C99 兼容性,则需要使用平台相关的内置类型(如 char short int long 等)的简单 typedef 来定义这些类型。

4.3.2 部分 std::array 实现
// A partial implementation of <array>
#include <algorithm>
#include <cstddef>
namespace std
{
    template <typename T, size_t N>
    struct array
    {
        // Type definitions:
        typedef T& reference;
        typedef const T& const_reference;
        typedef T* iterator;
        typedef const T* const_iterator;
        typedef size_t size_type;
        typedef ptrdiff_t difference_type;
        typedef T value_type;
        typedef T* pointer;
        typedef const T* const_pointer;
        // Data elements:
        T elems[N];
        // iterators:
        iterator begin() { return elems; }
        iterator end() { return elems + N; }
        const_iterator begin() const { return elems; }
        const_iterator end() const { return elems + N; }
        const_iterator cbegin() const { return elems; }
        const_iterator cend() const { return elems + N; }
        // Size-related members:
        constexpr size_type size() { return N; }
        constexpr size_type max_size() { return N; }
        constexpr bool empty() { return false; }
        // Element access members:
        reference operator[](size_type n) { return elems[n]; }
        const_reference operator[](size_type n) const { return elems[n]; }
        const_reference at(size_type n) const { return elems[n]; }
        reference at(size_type n) { return elems[n]; }
        reference front() { return elems[0U]; }
        const_reference front() const { return elems[0U]; }
        reference back() { return elems[N - 1U]; }
        const_reference back() const { return elems[N - 1U]; }
        T* data() { return elems; }
        const T* data() const { return elems; }
        // Element manipulation members:
        void fill(const T& u) { fill_n(begin(), N, u); }
        void swap(const array<T, N>& other) { swap_ranges(begin(), end(), other.begin()); }
    };
}
4.3.3 <algorithm> 库部分函数实现
  • std::min() std::max()
// Implement part of <algorithm>
namespace std
{
    // Sample implementation of std::min.
    template<typename T>
    const T& min(const T& a, const T& b)
    {
        return (a < b ? a : b);
    }
    // Sample implementation of std::max.
    template<typename T>
    const T& max(const T& a, const T& b)
    {
        return (a > b ? a : b);
    }
}
  • std::fill()
// Implement part of <algorithm>
namespace std
{
    // Sample implementation of std::fill.
    template<typename forward_iterator,
             typename value_type>
    void std::fill(forward_iterator first,
                   forward_iterator last,
                   const value_type& value)
    {
        // Fill each element in [first, last) with value.
        while(first != last)
        {
            *first = value;
            ++first;
        }
    }
}
  • std::for_each()
// Implement part of <algorithm>
namespace std
{
    // Sample implementation of std::for_each.
    template<typename iterator_type,
             typename function_type>
    function_type std::for_each(iterator_type first,
                                iterator_type last,
                                function_type function)
    {
        // Apply function to each element in [first, last).
        while(first != last)
        {
            function(*first);
            ++first;
        }
        return function;
    }
}
  • std::find_if()
// Implement part of <algorithm>
namespace std
{
    // Sample implementation of std::find_if.
    template<typename iterator_type,
             typename predicate_type>
    iterator_type std::find_if(iterator_type first,
                               iterator_type last,
                               predicate_type predicate)
    {
        // Find the first element satisfying predicate.
        while(
            (first != last)
            && (false == predicate(*first)))
        {
            ++first;
        }
        return first;
    }
}
4.3.4 <type_traits> 库部分实现
// Implement part of <type_traits>
namespace std
{
    template<typename integral_value_type,
             integral_value_type integral_value>
    struct integral_constant
    {
        typedef integral_value_type value_type;
        static constexpr value_type my_value =
            integral_value;
        typedef
            integral_constant<value_type, my_value> type;
        operator value_type() const
        {
            return my_value;
        }
    };
    typedef integral_constant<bool, true>
        true_type;
    typedef integral_constant<bool, false> false_type;
}
4.3.5 <cmath> 库部分实现
// Implement part of <cmath>
#include <math.h>
namespace std
{
    // Overwrites of the square root function.
    inline float sqrt(float x)
    {
        return ::sqrtf(x);
    }
    inline double sqrt(double x)
    {
        return ::sqrt(x);
    }
    inline long double sqrt(long double x)
    {
        return ::sqrtl(x);
    }
    // Overwrites of the sine function.
    inline float sin(float x)
    {
        return ::sinf(x);
    }
    inline double sin(double x)
    {
        return ::sin(x);
    }
    inline long double sin(long double x)
    {
        return ::sinl(x);
    }
    // Overwrites of the exponent function.
    inline float exp(float x)
    {
        return ::expf(x);
    }
    inline double exp(double x)
    {
        return ::exp(x);
    }
    inline long double exp(long double x)
    {
        return ::expl(x);
    }
    // ...and numerous other elementary functions.
}

4.4 手动实现标准库组件流程

graph TD;
    A[确定缺失组件] --> B[选择存储位置];
    B --> C[选择实现组件];
    C --> D[编写组件代码];
    D --> E[测试和调试];

通过以上内容,我们了解了 Boost 中的循环缓冲区类、自定义 dynamic_array 容器以及手动实现 C++ 标准库部分组件的方法,这些技术可以帮助我们在不同的编程场景中更好地使用 C++ 语言。

5. 手动实现组件的详细分析

5.1 固定大小整数类型实现分析

在实现固定大小整数类型时,根据编译器是否支持 C99 兼容性有不同的处理方式。
- 支持 C99 兼容性 :可以直接将 C99 固定大小整数类型注入到 std 命名空间,这种方式简单直接,利用了编译器已有的支持,代码如下:

// A partial implementation of <cstdint>
// Include the C99 fixed-size integers.
#include <stdint.h>
namespace std
{
    // Types with an exact number of bits.
    using ::uint8_t;
    using ::uint16_t;
    using ::uint32_t;
    using ::uint64_t;
    // Types with at least a certain number of bits.
    using ::uint_least8_t;
    using ::uint_least16_t;
    using ::uint_least32_t;
    using ::uint_least64_t;
    // Fastest types with at least a certain
    // number of bits.
    using ::uint_fast8_t;
    using ::uint_fast16_t;
    using ::uint_fast32_t;
    using ::uint_fast64_t;
}
  • 不支持 C99 兼容性 :需要使用平台相关的内置类型(如 char short int long 等)的简单 typedef 来定义这些类型。但这种方式存在轻微的可移植性问题,因为内置类型的宽度是编译器依赖的。不过,对于特定平台,只需设置一次这些类型,之后可以通过不同目录分离处理器特定版本的头文件,或使用预处理器定义在更大的头文件中实现分离。

5.2 std::array 部分实现分析

部分实现的 std::array 不包括反向迭代器,但满足了大部分基本功能。它使用了 std::size_t std::ptrdiff_t 等标准库类型,以及 std::fill_n() std::swap_ranges() 等算法。以下是部分关键代码分析:

// A partial implementation of <array>
#include <algorithm>
#include <cstddef>
namespace std
{
    template <typename T, size_t N>
    struct array
    {
        // 类型定义
        typedef T& reference;
        typedef const T& const_reference;
        typedef T* iterator;
        typedef const T* const_iterator;
        typedef size_t size_type;
        typedef ptrdiff_t difference_type;
        typedef T value_type;
        typedef T* pointer;
        typedef const T* const_pointer;
        // 数据元素
        T elems[N];
        // 迭代器
        iterator begin() { return elems; }
        iterator end() { return elems + N; }
        const_iterator begin() const { return elems; }
        const_iterator end() const { return elems + N; }
        const_iterator cbegin() const { return elems; }
        const_iterator cend() const { return elems + N; }
        // 大小相关成员
        constexpr size_type size() { return N; }
        constexpr size_type max_size() { return N; }
        constexpr bool empty() { return false; }
        // 元素访问成员
        reference operator[](size_type n) { return elems[n]; }
        const_reference operator[](size_type n) const { return elems[n]; }
        const_reference at(size_type n) const { return elems[n]; }
        reference at(size_type n) { return elems[n]; }
        reference front() { return elems[0U]; }
        const_reference front() const { return elems[0U]; }
        reference back() { return elems[N - 1U]; }
        const_reference back() const { return elems[N - 1U]; }
        T* data() { return elems; }
        const T* data() const { return elems; }
        // 元素操作成员
        void fill(const T& u) { fill_n(begin(), N, u); }
        void swap(const array<T, N>& other) { swap_ranges(begin(), end(), other.begin()); }
    };
}
  • 类型定义 :定义了各种常用的类型别名,方便使用。
  • 迭代器 :提供了 begin() end() 等迭代器函数,用于遍历数组元素。
  • 大小相关成员 :通过 size() max_size() 函数返回数组的大小, empty() 函数判断数组是否为空。
  • 元素访问成员 :提供了 operator[] at() 等函数用于访问数组元素。
  • 元素操作成员 fill() 函数用于用指定值填充数组, swap() 函数用于交换两个数组的元素。

5.3 <algorithm> 库部分函数实现分析

5.3.1 std::min() std::max()
// Implement part of <algorithm>
namespace std
{
    // Sample implementation of std::min.
    template<typename T>
    const T& min(const T& a, const T& b)
    {
        return (a < b ? a : b);
    }
    // Sample implementation of std::max.
    template<typename T>
    const T& max(const T& a, const T& b)
    {
        return (a > b ? a : b);
    }
}

这两个函数通过模板实现,比较两个元素的大小并返回较小或较大的元素,逻辑简单直接。

5.3.2 std::fill()
// Implement part of <algorithm>
namespace std
{
    // Sample implementation of std::fill.
    template<typename forward_iterator,
             typename value_type>
    void std::fill(forward_iterator first,
                   forward_iterator last,
                   const value_type& value)
    {
        // Fill each element in [first, last) with value.
        while(first != last)
        {
            *first = value;
            ++first;
        }
    }
}

该函数使用迭代器遍历指定范围 [first, last) ,并将每个元素赋值为指定的值。

5.3.3 std::for_each()
// Implement part of <algorithm>
namespace std
{
    // Sample implementation of std::for_each.
    template<typename iterator_type,
             typename function_type>
    function_type std::for_each(iterator_type first,
                                iterator_type last,
                                function_type function)
    {
        // Apply function to each element in [first, last).
        while(first != last)
        {
            function(*first);
            ++first;
        }
        return function;
    }
}

std::for_each() 函数对指定范围 [first, last) 内的每个元素应用给定的函数,并返回该函数对象。

5.3.4 std::find_if()
// Implement part of <algorithm>
namespace std
{
    // Sample implementation of std::find_if.
    template<typename iterator_type,
             typename predicate_type>
    iterator_type std::find_if(iterator_type first,
                               iterator_type last,
                               predicate_type predicate)
    {
        // Find the first element satisfying predicate.
        while(
            (first != last)
            && (false == predicate(*first)))
        {
            ++first;
        }
        return first;
    }
}

该函数在指定范围 [first, last) 内查找第一个满足给定谓词的元素,并返回该元素的迭代器。

5.4 <type_traits> 库部分实现分析

// Implement part of <type_traits>
namespace std
{
    template<typename integral_value_type,
             integral_value_type integral_value>
    struct integral_constant
    {
        typedef integral_value_type value_type;
        static constexpr value_type my_value =
            integral_value;
        typedef
            integral_constant<value_type, my_value> type;
        operator value_type() const
        {
            return my_value;
        }
    };
    typedef integral_constant<bool, true>
        true_type;
    typedef integral_constant<bool, false> false_type;
}

通过定义 integral_constant 结构体,可以方便地创建编译时常量。 true_type false_type 是基于 integral_constant 的特化,用于表示布尔常量。利用这些基础结构,可以编写许多编译时模板,如 std::is_integral 等。

5.5 <cmath> 库部分实现分析

// Implement part of <cmath>
#include <math.h>
namespace std
{
    // Overwrites of the square root function.
    inline float sqrt(float x)
    {
        return ::sqrtf(x);
    }
    inline double sqrt(double x)
    {
        return ::sqrt(x);
    }
    inline long double sqrt(long double x)
    {
        return ::sqrtl(x);
    }
    // Overwrites of the sine function.
    inline float sin(float x)
    {
        return ::sinf(x);
    }
    inline double sin(double x)
    {
        return ::sin(x);
    }
    inline long double sin(long double x)
    {
        return ::sinl(x);
    }
    // Overwrites of the exponent function.
    inline float exp(float x)
    {
        return ::expf(x);
    }
    inline double exp(double x)
    {
        return ::exp(x);
    }
    inline long double exp(long double x)
    {
        return ::expl(x);
    }
    // ...and numerous other elementary functions.
}

<cmath> 库的部分实现通过内联函数将 C 标准库中的数学函数包装到 std 命名空间中,为 C++ 提供了与 C 兼容的数学函数接口。

5.6 手动实现组件的优缺点总结

优点 缺点
可以在编译器不提供完整标准库时使用所需功能 手动实现可能存在错误,需要进行充分测试
可以根据实际需求选择实现部分组件,减少代码量 部分实现可能不完整,无法涵盖标准库的所有功能
加深对标准库实现原理的理解 对于复杂组件的实现难度较大

6. 总结与应用建议

6.1 总结

本文介绍了 Boost 中的循环缓冲区类、自定义 dynamic_array 容器以及手动实现 C++ 标准库部分组件的方法。
- Boost 循环缓冲区类 :提供了支持迭代器且符合 STL 标准的循环缓冲区功能,使用简单。
- 自定义 dynamic_array 容器 :结合了 std::array std::vector 的优点,在构造时动态分配大小,之后保持不变,可与 STL 算法一起使用。
- 手动实现标准库组件 :在编译器不提供完整标准库时,可以根据实用性和实现难度选择部分组件进行手动实现。

6.2 应用建议

  • 选择合适的容器 :根据具体需求选择 std::array std::vector dynamic_array 容器。如果需要固定大小的容器,使用 std::array ;如果需要动态调整大小,使用 std::vector ;如果需要在构造时动态分配大小且之后保持不变,使用 dynamic_array
  • 手动实现标准库组件 :在编译器不提供完整标准库时,优先选择实现常用且易于实现的组件,如固定大小整数类型、部分 std::array 实现、 <algorithm> 库部分函数等。在实现过程中,要注意可移植性和代码的正确性,进行充分的测试和调试。

通过合理运用这些技术,可以在不同的编程场景中更好地使用 C++ 语言,提高编程效率和代码质量。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值