C++证道之路第三章字符串、向量和数组

一、命名空间的using声明

在C++中,命名空间是一种将标识符(如变量、函数、类等)组织到一个单独的作用域中的机制。当我们在大型项目中工作时,可能会遇到命名冲突的问题,这时命名空间就非常有用。

然而,在使用命名空间中的成员时,需要在每个成员前加上命名空间的名字,这可能会使代码变得冗长。为了解决这个问题,C++提供了using声明和using指令。

using声明:它用于从命名空间中引入单个标识符。例如:

namespace MyNamespace {
    void myFunction() { /* ... */ }
}

int main() {
    using MyNamespace::myFunction; // 使用using声明从MyNamespace中引入myFunction
    myFunction(); // 现在可以直接调用myFunction,不需要加上MyNamespace::
    return 0;
}

 

using指令:它用于从命名空间中引入所有标识符。这通常在头文件中使用,以避免每次使用命名空间中的成员时都必须加上命名空间的名字。但是,如果命名空间很大或包含有命名冲突的标识符,这样做可能会导致问题。

namespace MyNamespace {
    void myFunction() { /* ... */ }
}

int main() {
    using namespace MyNamespace; // 使用using指令从MyNamespace中引入所有标识符
    myFunction(); // 现在可以直接调用myFunction,不需要加上MyNamespace::
    return 0;
}

二、标准库类型string

1定义和初始化string对象

在C++中,std::string 是一个非常强大的字符串处理工具,位于 <string> 标准库中。定义和初始化 std::string 的方式如下:

#include <string>

// 默认构造函数
std::string str1;

// 基于C风格字符串初始化
const char *cstr = "Hello, World!";
std::string str2(cstr);

// 初始化为指定长度和字符
std::string str3(10, 'a'); // "aaaaaaaaaa"

// 初始化为另一个string对象的副本
std::string str4(str2);

// 初始化为另一个string对象的子串
std::string str5(str2, 7, 5); // "World"
2string对象上的操作

std::string 提供了丰富的操作接口,包括但不限于:

  • 连接

    1std::string str6 = str1 + str2;
  • 比较

    1if (str1 == str2) {
    2    // 相等
    3} else if (str1 < str2) {
    4    // str1小于str2
    5}
  • 查找

    1size_t pos = str1.find("world"); // 查找子串位置
  • 替换

    1str1.replace(pos, 5, "universe");
  • 插入

    1str1.insert(pos, "extra ");
  • 删除

    1str1.erase(pos, 5);
  • 大小

    1size_t len = str1.length();
  • 访问元素

    1char ch = str1.at(pos); // 或者 str1[pos]
3处理string对象中的字符
std::string str = "Hello";
str[0] = 'h'; // 修改第一个字符为小写
char firstChar = str[0]; // 获取第一个字符

使用 at() 方法更安全,因为它会在访问越界时抛出异常:、
try {
    char ch = str.at(10); // 如果str长度小于10,会抛出std::out_of_range异常
} catch (std::out_of_range& e) {
    std::cerr << "Error: " << e.what() << std::endl;
}

三、标准库类型vector

std::vector 是 C++ 标准模板库 (STL) 中的一个动态数组容器。它可以自动调整其内部存储空间的大小,以便容纳更多的元素。

1定义和初始化vector对象
#include <vector>
#include <iostream>

int main() {
    // 默认构造,创建空的 vector
    std::vector<int> v1;

    // 构造并初始化为特定大小和值
    std::vector<int> v2(10, 1); // 创建一个包含10个元素的vector,每个元素值为1

    // 构造并初始化为另一个vector的副本
    std::vector<int> v3(v2);

    // 构造并初始化为一系列初始值
    std::vector<int> v4{1, 2, 3, 4, 5};

    // 构造并初始化为迭代器范围内的元素
    int arr[] = {1, 2, 3, 4, 5};
    std::vector<int> v5(arr, arr + sizeof(arr) / sizeof(arr[0]));
    
    return 0;
}
2向vector对象中添加元素
std::vector<int> v;

// 在向量末尾添加元素
v.push_back(10); // 添加整数10

// 使用迭代器在向量末尾添加多个元素
std::vector<int> v2{1, 2, 3};
v.insert(v.end(), v2.begin(), v2.end());

// 在指定位置插入元素
v.insert(v.begin(), 0); // 在向量开始处插入0

// 使用 emplace_back 和 emplace 在运行时直接在向量中构造对象
std::vector<std::string> vs;
vs.emplace_back("Hello"); // 直接在向量中构造字符串对象
3其他vector操作
std::vector<int> v = {1, 2, 3, 4, 5};

// 访问元素
int first = v.front(); // 获取第一个元素
int last = v.back(); // 获取最后一个元素

// 删除元素
v.pop_back(); // 删除最后一个元素
v.erase(v.begin()); // 删除第一个元素

// 清空向量
v.clear();

// 检查是否为空
bool empty = v.empty();

// 获取大小和容量
size_t size = v.size();
size_t capacity = v.capacity();

// 重新分配大小
v.resize(10); // 将向量大小设置为10
v.resize(5, -1); // 将向量大小设置为5,新位置的元素值为-1

// 交换两个向量的内容
std::vector<int> v2 = {6, 7, 8};
v.swap(v2);

四、迭代器介绍

迭代器的工作方式类似于指针,但它们被设计成可以与更广泛的容器一起工作,而不仅仅是原始数组。

1使用迭代器
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 定义迭代器
    std::vector<int>::iterator it; // 或简写为 auto it
}

// 初始化迭代器指向容器的第一个元素
it = vec.begin();

// 初始化迭代器指向容器的最后一个元素后的下一个位置
it = vec.end();

// 输出容器中的元素
for (it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << " ";
}

for (auto &element : vec) {
    std::cout << element << " ";
}

2迭代器运算

迭代器支持几种运算,这使得它们在遍历容器和执行操作时更加灵活。

it++; // 移动到下一个元素
it--; // 移动到前一个元素

if (it1 == it2) {} // 判断两个迭代器是否指向同一位置
if (it1 != it2) {} // 判断两个迭代器是否不指向同一位置

int distance = it2 - it1; // 计算it1和it2之间的距离

it = it1 + 5; // 移动迭代器5个位置
it = it1 - 2; // 移动迭代器2个位置

五、数组

1定义和初始化内置数组
// 定义一个整型数组,包含5个元素
int arr[5];

// 初始化数组
int arr2[5] = {1, 2, 3, 4, 5}; // 显式初始化所有元素
int arr3[5] = {1, 2, 3};       // 隐式初始化剩余元素为0

// C++11 及以后版本的初始化语法
int arr4[] = {1, 2, 3, 4, 5};
int arr5[5] = {0};             // 所有元素初始化为0
2访问数组元素
arr[0] = 10; // 设置第一个元素的值
int val = arr[4]; // 获取第五个元素的值
3指针和数组
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // ptr 现在指向 arr[0]

// 通过指针访问元素
std::cout << *ptr << std::endl; // 输出 arr[0]
std::cout << *(ptr + 1) << std::endl; // 输出 arr[1]
4C风格字符串
char str[] = "Hello, World!"; // C风格字符串
std::cout << str << std::endl; // 输出字符串

// 访问字符串中的字符
char ch = str[0]; // 获取第一个字符 'H'
5与旧代码的接口
void c_function(char *s) {
    while (*s) {
        std::cout << *s++;
    }
}

int main() {
    char str[] = "Hello from C function";
    c_function(str); // 调用C风格的函数
    return 0;
}

 现代C++鼓励使用std::arraystd::vector等更安全、更灵活的容器,因为它们提供了额外的功能并且可以自动管理内存。然而,在某些情况下,使用内置数组和C风格字符串仍然是必要的,尤其是在需要与C库交互或者性能优化要求极高的场景下

六、多维数组

多维数组在内存中是连续存储的,这意味着如果要改变数组的大小,你可能需要重新分配内存,这在C++中通常不是自动的,除非使用std::vectorstd::array等容器。

C++编译器在编译时会检查数组索引是否越界,但在运行时不会,因此你需要确保你的索引值不会超出数组的界限。

多维数组的内存消耗比一维数组大,因为它们通常用于存储大量数据。在资源有限的环境中,应谨慎使用。

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 11:14:03       99 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 11:14:03       107 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 11:14:03       90 阅读
  4. Python语言-面向对象

    2024-07-10 11:14:03       98 阅读

热门阅读

  1. PHP的发展历程以及功能使用场景

    2024-07-10 11:14:03       32 阅读
  2. Redis哨兵模式与集群模式的快速部署

    2024-07-10 11:14:03       25 阅读
  3. 单片机与FPGA的关系及其在嵌入式系统中的应用

    2024-07-10 11:14:03       32 阅读
  4. Hadoop中的副本、校验和(数字指纹)、block

    2024-07-10 11:14:03       21 阅读
  5. Xshell 和宝塔有啥区别

    2024-07-10 11:14:03       30 阅读
  6. SD卡,laptop,启动ubtuntu

    2024-07-10 11:14:03       26 阅读