33、C++ 编程基础与高级特性解析

C++ 编程基础与高级特性解析

1. 参考资料概述

在 C++ 编程的学习和实践中,有许多有价值的参考资料,以下为部分列举:
|序号|作者|标题|出版信息|
| ---- | ---- | ---- | ---- |
|1|D. Abrahams, A. Gurtovoy|C++ Template Metaprogramming: Concepts, Tools and Techniques from Boost and Beyond|Addison - Wesley, Boston, 2004|
|2|ANSI|ANSI X3.159 - 1989 American National Standard for Information Systems – Programming Language C|American National Standard for Information, 1989|
|3|ARDUINO®|ARDUINO®|2015. http://www.arduino.cc|
|4|M. Barr|Programming Embedded Systems with C and GNU Development Tools, Second Edition|O’Reilly, Sebastopol, 2006|
|5|P. Becker|The C++ Standard Library Extensions: A Tutorial and Reference|Addison - Wesley, Boston, 2006|

2. C++ 基础特性

2.1 C++ 类型转换运算符

C++ 有四种模板类型转换运算符,下面介绍其中两种:
- static_cast 运算符 :用于较为常规的类型转换,例如从 float 类型转换为 int 类型。示例代码如下:

// appendix0a_01 - 001_static_cast.cpp
float f = 3.1415926535'8979323846'264338328F;
// The value of n is 3.
int n = static_cast<int>(f);
  • reinterpret_cast 运算符 :可以进行看似不相关类型之间的转换,如将地址的整数值转换为地址指针。不过该运算符具有非可移植性,使用时需要谨慎。以下是一个设置微控制器端口寄存器位的示例:
// appendix0a_01 - 002_reinterpret_cast.cpp
// The address of portb is 0x25.
constexpr std::uint8_t portb = UINT8_C(0x25);
// Cast std::uint8_t to std::uint8_t*.
volatile std::uint8_t* pb = reinterpret_cast<volatile std::uint8_t*>(portb);
// Set portb.5.
*pb |= UINT8_C(0x20);

此外,C++ 还提供了 dynamic_cast 和 const_cast 运算符。dynamic_cast 常用于在类层次结构中转换指针和引用,会进行代价较高但可靠的运行时检查;const_cast 可以更改对象的常量或易变属性。

2.2 统一初始化语法

C++11 引入了统一初始化语法,它可以用于任何对象的初始化,能与传统的括号构造函数初始化和赋值运算符初始化一起使用。统一初始化语法使用花括号来包含初始值。以下是不同类型的初始化示例:
- 内置类型初始化

// appendix0a_02 - 001_uniform_initialization.cpp
int n { 123 };
float f { 3.1415926535'8979323846F };
  • 聚合类型初始化
// appendix0a_02 - 002_uniform_initialization.cpp
struct my_struct
{
    int my_n;
    float my_f;
    my_struct(const int n = 0, const float& f = 0.0F) : my_n(n), my_f(f) { }
};
my_struct instance
{
    123,
    // Initial value of n.
    3.1415926535'8979323846F // Initial value of f.
};
  • 编译器类型推导
// appendix0a_02 - 003_uniform_initialization.cpp
struct my_struct
{
    // ...
};
my_struct function()
{
    // The compiler correctly deduces the return type.
    return
    {
        456,
        0.5772156649'0153286061F
    };
}
  • 类类型初始化
// appendix0a_02 - 004_uniform_initialization.cpp
struct point
{
    point(const int x = 0, const int y = 0) : my_x{x}, my_y{y} { }
    int my_x;
    int my_y;
};
point pt
{
    123,
    456
};
  • STL 容器初始化
// appendix0a_02 - 005_uniform_initialization.cpp
std::array<int, 3U> a
{
    { 1, 2, 3 }
};
std::vector<char> v
{
    { 'a', 'b', 'c' }
};

2.3 函数重载

函数重载允许创建多个同名但输入和输出参数类型不同的函数。例如:

// appendix0a_03 - 001_function_overloading.cpp
// The area of a rectangle.
float area(const float& length, const float& width)
{
    return length * width;
}
// The area of a circle.
float area(const float& radius)
{
    constexpr float pi = 3.1415926535'8979323846F;
    return (pi * radius) * radius;
}

全局函数、局部函数和类成员函数都可以进行重载,但要注意不要将类成员重载与动态多态和运行时虚函数机制混淆。

2.4 编译时断言

static_assert 用于在编译时检查常量表达式。其语法如下:

// appendix0a_04 - 001_static_assert.cpp
static_assert(expression, message);

其中,expression 是要检查的条件,message 是可能有用的诊断文本。如果 expression 为真,编译继续进行;如果为假,则会产生编译器错误并显示消息文本。例如:

// appendix0a_04 - 002_static_assert.cpp
constexpr unsigned int version = 3U;
// Print error message if version is less than 2.
static_assert(version >= 2U, "Version is too low!");

在 C++17 中,static_assert 的错误文本可以省略,会提供默认文本。示例如下:

// appendix0a_04 - 003_static_assert.cpp
constexpr unsigned int version = 3U;
// Print default error message if version is not 3.
static_assert(version == 3U);

2.5 数值限制

C++ 标准库在 头文件中提供了 std::numeric_limits 模板,用于查询内置类型的数值限制。以下是该模板类的概要:

// appendix0a_05 - 001_numeric_limits.cpp
namespace std
{
    template<class T>
    class numeric_limits
    {
    public:
        static constexpr bool is_specialized = false;
        static constexpr T min() { return T(); }
        static constexpr T max() { return T(); }
        static constexpr T lowest() { return T(); }
        static constexpr int digits = 0;
        static constexpr int digits10 = 0;
        static constexpr int max_digits10 = 0;
        static constexpr bool is_signed = false;
        static constexpr bool is_integer = false;
        static constexpr bool is_exact = false;
        static constexpr int radix = 0;
        static constexpr T epsilon() { return T(); }
        static constexpr T round_error() { return T(); }
        static constexpr int min_exponent = 0;
        static constexpr int min_exponent10 = 0;
        static constexpr int max_exponent = 0;
        static constexpr int max_exponent10 = 0;
        static constexpr bool has_infinity = false;
        static constexpr bool has_quiet_NaN = false;
        static constexpr bool has_signaling_NaN = false;
        static constexpr float_denorm_style has_denorm = denorm_absent;
        static constexpr bool has_denorm_loss = false;
        static constexpr T infinity() { return T(); }
        static constexpr T quiet_NaN() { return T(); }
        static constexpr T signaling_NaN() { return T(); }
        static constexpr T denorm_min() { return T(); }
        static constexpr bool is_iec559 = false;
        static constexpr bool is_bounded = false;
        static constexpr bool is_modulo = false;
        static constexpr bool traps = false;
        static constexpr bool tinyness_before = false;
        static constexpr float_round_style round_style = round_toward_zero;
    };
}

对于 32 位 int 类型的特化示例如下:

// appendix0a_05 - 001_numeric_limits.cpp
namespace std
{
    template<>
    class numeric_limits<int>
    {
    public:
        static constexpr bool is_specialized = true;
        static constexpr int min() { return 0; }
        static constexpr int max() { return +2147483647; }
        static constexpr int lowest() { return -2147483648; }
        static constexpr int digits = 32;
        static constexpr int digits10 = 9;
        static constexpr int max_digits10 = 9;
        static constexpr bool is_signed = false;
        static constexpr bool is_integer = true;
        static constexpr bool is_exact = true;
        static constexpr int radix = 2;
        static constexpr int epsilon() { return 0; }
        static constexpr int round_error() { return 0; }
        static constexpr int min_exponent = 0;
        static constexpr int min_exponent10 = 0;
        static constexpr int max_exponent = 0;
        static constexpr int max_exponent10 = 0;
        static constexpr bool has_infinity = false;
        static constexpr bool has_quiet_NaN = false;
        static constexpr bool has_signaling_NaN = false;
        static constexpr float_denorm_style has_denorm = denorm_absent;
        static constexpr bool has_denorm_loss = false;
        static constexpr int infinity () { return 0; }
        static constexpr int quiet_NaN() { return 0; }
        static constexpr int signaling_NaN() { return 0; }
        static constexpr int denorm_min() { return 0; }
        static constexpr bool is_iec559 = false;
        static constexpr bool is_bounded = false;
        static constexpr bool is_modulo = false;
        static constexpr bool traps = false;
        static constexpr bool tinyness_before = false;
        static constexpr float_round_style round_style = round_toward_zero;
    };
}

使用示例:

// appendix0a_05 - 001_numeric_limits.cpp
constexpr int n_max = std::numeric_limits<int>::max();
constexpr int n_min = std::numeric_limits<int>::max();
constexpr int n_low = std::numeric_limits<int>::lowest();

数值限制还可以方便地用于其他模板中,例如:

// appendix0a_05 - 002_numeric_limits.cpp
template<typename unsigned_type>
struct hi_bit
{
    // The bit - position of the high bit.
    static constexpr int bpos = std::numeric_limits<unsigned_type>::digits - 1;
    // The value of the type with the high - bit set.
    static constexpr unsigned_type value = static_cast<unsigned_type>(1) << bpos;
};

可以根据不同的无符号类型生成编译时常量值:

// appendix0a_05 - 002_numeric_limits.cpp
constexpr std::uint8_t hi08 = hi_bit<std::uint8_t>::value;
// (1 << 7)
constexpr std::uint16_t hi16 = hi_bit<std::uint16_t>::value; // (1 << 15)
constexpr std::uint32_t hi32 = hi_bit<std::uint32_t>::value; // (1 << 31)
constexpr std::uint64_t hi64 = hi_bit<std::uint64_t>::value; // (1 << 63)

也可以编写 std::numeric_limits 的特化来提供用户自定义类型的数值限制信息。

2.6 STL 容器

C++ 标准库的 STL 包含多种容器类型,用于在单个对象中存储多个元素。以下是常见的 STL 容器及其特点:
|容器类型|特点|
| ---- | ---- |
|std::array|固定长度的顺序数组,在内存中连续对齐|
|std::vector|类似于 std::array,但长度可以动态改变,设计用于快速随机访问|
|std::deque|双端队列,适合在前后端进行快速插入和删除操作|
|std::list|可双向遍历的序列,但不支持随机访问,支持在序列中任意位置快速插入和删除|
|std::forward_list|类似于 std::list,但只能向前遍历|
|std::basic_string, std::string, std::wstring|用于存储基于字符的序列(即字符串),虽不完全符合顺序 STL 容器的正式要求,但常被视为顺序容器|

此外,STL 还包括关联容器(如 std::set、std::multiset、std::map 和 std::multimap)和标准适配器(如 std::stack、std::queue 和 std::priority_queue)。

以下是使用 std::vector 容器的示例:

// appendix0a_06 - 001_stl_containers.cpp
#include <vector>
void do_something()
{
    // Create v with three elements having value 0.
    std::vector<int> v(3U, 0);
    // Set the values in v to (1,2,3).
    v[0U] = 1;
    v[1U] = 2;
    v[2U] = 3;
    // The initial size is 3.
    std::vector<int>::size_type s = v.size();
    v.push_back(4);
    // The size is now 4.
    s = v.size();
    int x0 = v.at(0U); // Value of x0 is 1.
    int x3 = v.back(); // Value of x3 is 4.

    // Copy constructor from another vector.
    std::vector<int> v2(v);
    // Constructor from other input iterators.
    std::vector<int> v3(v.begin(), v.end());
    // Support for operator=().
    std::vector<int> v4 = v;
}

容器可以进行复制构造、从另一个迭代器序列创建和复制赋值。容器的其他成员函数包括访问函数和序列操作(如插入、赋值等)。模板容器使用成员类型定义来定义常见的成员类型,如 std::vector 的 size_type。

2.7 STL 迭代器

迭代器用于遍历顺序容器的元素并访问其值,可以读写标准 STL 容器的元素。每个标准 STL 容器都通过提供专用的迭代器类型和标准化的迭代器函数(如 begin() 和 end())来方便地操作其元素。

以下是使用 std::vector 迭代器的示例:

// appendix0a_07 - 001_stl_iterators.cpp
#include <vector>
void do_something()
{
    // Set v to (1,2,3).
    std::vector<int> v( { 1, 2, 3 } );
    // Declare an iterator for std::vector<int>.
    std::vector<int>::iterator it;
    // Add 5 to each element in v.
    for(it = v.begin(); it != v.end(); ++it)
    {
        *it += 5;
    }
    // Now v is (6,7,8).
}

迭代器对 [First, Last) 表示一个序列的范围,其中 First 指向序列的第一个元素,Last 指向最后一个元素的下一个位置。STL 的标准算法遵循此约定。例如,使用 std::copy() 函数复制元素:

// appendix0a_07 - 002_stl_iterators.cpp
#include <algorithm>
#include <array>
#include <vector>
void do_something()
{
    // Initialized src with (101, 101, 101).
    const std::vector<int> src(3U, 101);
    // Default - initialized dst.
    std::array<int, 3U> dst = {{ }};
    // Copy from vector src to array dst.
    // dst now also contains (101, 101, 101).
    std::copy(src.begin(), src.end(), dst.begin());
}

所有迭代器都支持递增(++)操作,部分支持递减(–)操作,一般前置递增和递减形式比后置形式更高效。STL 迭代器使用解引用运算符(*)或成员选择运算符(->)进行元素访问。C++ 有多种迭代器类别,如前向迭代器、双向迭代器和随机访问迭代器。同时,常量迭代器只能进行只读访问,非常量迭代器可以读写容器元素。

// appendix0a_07 - 003_stl_iterators.cpp
#include <array>
void do_something()
{
    using container_type = std::array<int, 3U>;
    using const_iterator_type = typename container_type::const_iterator;
    using iterator_type = typename container_type::iterator;
    container_type cnt = {{ 1, 2, 3 }};
    iterator_type nonconst_iterator = cnt.begin();
}

3. 总结与应用建议

3.1 特性总结

特性 描述 示例代码
类型转换运算符 包含 static_cast、reinterpret_cast、dynamic_cast 和 const_cast 四种。static_cast 用于常规类型转换,reinterpret_cast 可进行看似不相关类型转换,dynamic_cast 用于类层次结构中指针和引用转换,const_cast 可更改对象常量或易变属性 cpp<br>float f = 3.1415926535'8979323846'264338328F;<br>int n = static_cast<int>(f);<br>constexpr std::uint8_t portb = UINT8_C(0x25);<br>volatile std::uint8_t* pb = reinterpret_cast<volatile std::uint8_t*>(portb);<br>
统一初始化语法 C++11 引入,使用花括号包含初始值,可用于内置类型、聚合类型、类类型和 STL 容器初始化,还能让编译器推导类型 cpp<br>int n { 123 };<br>struct my_struct { ... };<br>my_struct instance { 123, 3.1415926535'8979323846F };<br>std::array<int, 3U> a { { 1, 2, 3 } };<br>
函数重载 允许创建多个同名但输入和输出参数类型不同的函数,全局、局部和类成员函数均可重载 cpp<br>float area(const float& length, const float& width) { return length * width; }<br>float area(const float& radius) { constexpr float pi = 3.1415926535'8979323846F; return (pi * radius) * radius; }<br>
编译时断言 static_assert 用于在编译时检查常量表达式,若表达式为假会产生编译器错误并显示消息文本,C++17 中错误文本可省略 cpp<br>constexpr unsigned int version = 3U;<br>static_assert(version >= 2U, "Version is too low!");<br>static_assert(version == 3U);<br>
数值限制 C++ 标准库在 头文件中提供 std::numeric_limits 模板,用于查询内置类型的数值限制,也可编写特化提供用户自定义类型的数值限制信息 cpp<br>constexpr int n_max = std::numeric_limits<int>::max();<br>template<typename unsigned_type><br>struct hi_bit { ... };<br>
STL 容器 包含顺序容器(如 std::array、std::vector、std::deque、std::list、std::forward_list、std::string 等)、关联容器和标准适配器,用于在单个对象中存储多个元素,各有特点 cpp<br>std::vector<int> v(3U, 0);<br>v[0U] = 1; v[1U] = 2; v[2U] = 3; v.push_back(4);<br>
STL 迭代器 用于遍历顺序容器的元素并访问其值,每个标准 STL 容器提供专用迭代器类型和标准化迭代器函数,迭代器对 [First, Last) 表示序列范围,有多种迭代器类别,常量迭代器只读,非常量迭代器可读写 cpp<br>std::vector<int> v( { 1, 2, 3 } );<br>std::vector<int>::iterator it;<br>for(it = v.begin(); it != v.end(); ++it) { *it += 5; }<br>

3.2 应用建议

  • 类型转换运算符 :在进行常规类型转换时优先使用 static_cast,对于底层操作(如直接访问寄存器)可谨慎使用 reinterpret_cast。避免过度使用 dynamic_cast,因为其运行时检查会带来一定性能开销。
  • 统一初始化语法 :尽量使用统一初始化语法,它能提高代码的一致性和可读性,尤其在初始化 STL 容器时非常方便。
  • 函数重载 :合理使用函数重载可以提高代码的可读性和可维护性,但要注意避免重载过于复杂导致代码难以理解。
  • 编译时断言 :在代码中使用编译时断言可以提前发现一些潜在的错误,特别是在处理平台特定要求或常量表达式检查时非常有用。
  • 数值限制 :在编写涉及数值计算的代码时,使用 std::numeric_limits 可以避免数值溢出等问题,同时可以方便地进行模板编程。
  • STL 容器 :根据具体的编程需求选择合适的 STL 容器。如果需要快速随机访问,可选择 std::array 或 std::vector;如果需要频繁在前后端插入和删除元素,可选择 std::deque;如果需要在序列中任意位置快速插入和删除,可选择 std::list。
  • STL 迭代器 :熟练掌握迭代器的使用,利用迭代器可以方便地遍历和操作容器元素,同时要注意常量迭代器和非常量迭代器的区别。

3.3 操作流程总结

以下是使用 STL 容器和迭代器的一般操作流程:

graph LR
    A[创建容器] --> B[初始化容器元素]
    B --> C[使用迭代器遍历容器]
    C --> D[对容器元素进行操作]
    D --> E[根据需要修改容器大小或内容]
    E --> F[使用容器的其他成员函数进行操作]

通过以上对 C++ 编程基础与高级特性的解析,希望能帮助大家更好地理解和运用这些特性,编写出更高效、更健壮的 C++ 代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值