C++ Vector容器详解:一站式指南,掌握动态数组的高效使用

简介: C++ Vector容器详解:一站式指南,掌握动态数组的高效使用

引言

C++容器是一种非常有用的数据结构,它们可以存储和管理不同类型的数据。在现代C++编程中,容器的概念已经深入人心,其中Vector容器是最常用和最受欢迎的一种容器。本文将从容器的概念、Vector容器的基础知识和应用场景入手,详细介绍Vector容器的各个方面。

C++容器的概念与作用

C++容器是用来存储和管理数据的一种结构。它们可以存储不同类型的数据,并且可以动态调整大小以适应不断变化的需求。容器的主要作用是提供一种易于使用和高效的数据存储和操作手段。

  • Vector容器在现代C++编程中的重要性

Vector容器作为一种动态数组,其内存管理、性能优化等特点使其成为现代C++编程中最常用的容器之一。

Vector容器提供了许多高效的成员函数,便于对数据进行快速访问和操作。

  • Vector容器的应用场景

Vector容器广泛应用于各种场景,如实现动态数组、字符串处理、矩阵计算、图形和几何处理等。由于Vector容器的灵活性和性能优势,它在各类编程问题中都能发挥重要作用。

Vector容器基础

Vector容器的定义与语法

Vector容器是一种动态数组,可以容纳任意数量的同一类型的元素。使用C++标准模板库(STL)中的头文件定义Vector容器,其基本语法如下:

#include <vector>
std::vector<元素类型> vector_name;

动态内存分配与自动扩容

Vector容器内部采用动态内存分配,能够根据需要自动调整其大小。当向Vector容器中添加元素时,如果容器的大小超过了当前容量,Vector会自动扩容,通常以当前容量的两倍进行扩展。

常用成员函数及其作用

  1. 构造函数:
  • vector v;:创建一个空的vector对象,元素类型为T。
  • vector v(n);:创建一个具有n个元素的vector对象,元素类型为T。
  • vector v(n, value);:创建一个具有n个元素的vector对象,元素类型为T,所有元素初始值为value。
  1. assign():为vector分配新的元素并替换当前的元素。
  • v.assign(n, value);:将vector的大小调整为n,并将所有元素设置为value。
  • v.assign(first, last);:用迭代器范围[first, last)中的元素替换当前vector的内容。
  1. push_back():在vector的末尾添加一个元素。
  • v.push_back(value);:将value添加到vector的末尾。
  1. pop_back():删除vector末尾的元素。
  • v.pop_back();:删除vector末尾的元素。
  1. insert():在指定位置插入元素。
  • v.insert(pos, value);:在pos位置插入一个value元素。
  • v.insert(pos, n, value);:在pos位置插入n个value元素。
  • v.insert(pos, first, last);:在pos位置插入迭代器范围[first, last)内的元素。
  1. erase():删除指定位置的元素或一定范围内的元素。
  • v.erase(pos);:删除pos位置的元素。
  • v.erase(first, last);:删除迭代器范围[first, last)内的元素。
  1. clear():清除vector中的所有元素。
  • v.clear();:清空vector。
  1. size():返回vector中元素的个数。
  • v.size();:获取vector的大小。
  1. empty():检查vector是否为空。
  • v.empty();:若vector为空,返回true;否则返回false。
  1. resize():改变vector的大小。
  • v.resize(n);:将vector的大小调整为n。
  • v.resize(n, value);:将vector的大小调整为n,新增的元素初始值为value。
  1. capacity():返回vector的容量。
  • v.capacity();:获取vector的容量。
  1. reserve():改变vector的容量。
  • v.reserve(n);:将vector的容量调整至至少为n,如果当前容量大于或等于n,则不改变。
  1. shrink_to_fit():将vector的容量收缩至与其大小相同。
  • v.shrink_to_fit();:收缩vector的容量。
  1. 访问元素:
  • v.at(index);:返回位于给定索引位置的元素的引用。如果索引越界,则抛出一个out_of_range异常。
  • v[index];:返回位于给定索引位置的元素的引用。但是不进行越界检查。
  • v.front();:返回vector首元素的引用。
  • v.back();:返回vector末尾元素的引用。
  1. 迭代器相关:
  • v.begin();:返回指向vector第一个元素的迭代器。
  • v.end();:返回指向vector最后一个元素之后的迭代器。
  • v.rbegin();:返回指向vector最后一个元素的反向迭代器。
  • v.rend();:返回指向vector第一个元素之前的反向迭代器。
  • v.cbegin();:返回指向vector第一个元素的常量迭代器。
  • v.cend();:返回指向vector最后一个元素之后的常量迭代器。
  • v.crbegin();:返回指向vector最后一个元素的常量反向迭代器。
  • v.crend();:返回指向vector第一个元素之前的常量反向迭代器。
  1. swap():交换两个vector对象的内容。
  • v1.swap(v2);:将v1和v2的内容互换。
  1. emplace():在指定位置构造一个新元素。
  • v.emplace(pos, args);:在pos位置使用args参数构造一个新元素。
  1. emplace_back():在vector末尾构造一个新元素。
  • v.emplace_back(args);:在vector末尾使用args参数构造一个新元素。
  1. max_size():返回vector在内存中可能容纳的最大元素数量。
  • v.max_size();:获取vector的最大容量。
  1. 比较操作符:
  • v1 == v2;:判断v1和v2是否相等。
  • v1 != v2;:判断v1和v2是否不相等。
  • v1 < v2;:判断v1是否小于v2。
  • v1 <= v2;:判断v1是否小于等于v2。
  • v1 > v2;:判断v1是否大于v2。
  • v1 >= v2;:判断v1是否大于等于v2。

以上就是vector容器的一些常用成员函数及其作用。这些成员函数可以帮助我们在实际编程中进行动态数组的创建、修改、访问和操作。

Vector容器的迭代器

迭代器(Iterator)是一种用于访问和遍历容器中元素的对象。它可以将访问的复杂性隐藏在底层实现中,提供了类似于指针的访问和操作功能。

迭代器的概念与分类

迭代器根据访问和操作的权限,可以分为以下几类:

  1. 输入迭代器(Input Iterator):用于读取顺序访问的元素。
  2. 输出迭代器(Output Iterator):用于写入顺序访问的元素。
  3. 前向迭代器(Forward Iterator):支持输入和输出迭代器的功能,还可以多次遍历。
  4. 双向迭代器(Bidirectional Iterator):支持前向迭代器的功能,还可以向前和向后遍历。
  5. 随机访问迭代器(Random Access Iterator):支持双向迭代器的功能,还可以进行随机访问。

Vector容器的迭代器类型

Vector容器支持以下迭代器类型:

  1. 正向迭代器(iterator):双向迭代器,可以向前和向后遍历元素。
  2. 反向迭代器(reverse_iterator):双向迭代器,从后向前遍历元素。
  3. 常量正向迭代器(const_iterator):只读迭代器,不能修改元素值。
  4. 常量反向迭代器(const_reverse_iterator):只读反向迭代器,不能修改元素值。

使用迭代器遍历和修改Vector容器内容

以下示例展示了如何使用迭代器遍历和修改vector容器的内容:

//我们使用了正向迭代器、反向迭代器、常量正向迭代器和常量反向迭代器分别遍历vector容器。正向迭代器和反向迭代器可以用于修改元素值,而常量正向迭代器和常量反向迭代器则仅用于访问元素值。
#include <iostream>
#include <vector>
int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    // 使用正向迭代器遍历和修改元素值
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        *it *= 2; // 修改元素值
        std::cout << *it << " "; // 输出元素值
    }
    std::cout << std::endl;
    // 使用反向迭代器遍历元素
    for (auto rit = vec.rbegin(); rit != vec.rend(); ++rit) {
        std::cout << *rit << " "; // 输出元素值
    }
    std::cout << std::endl;
    // 使用常量正向迭代器遍历元素
    for (auto cit = vec.cbegin(); cit != vec.cend(); ++cit) {
        std::cout << *cit << " "; // 输出元素值
    }
    std::cout << std::endl;
    // 使用常量反向迭代器遍历元素
    for (auto crit = vec.crbegin(); crit != vec.crend(); ++crit) {
        std::cout << *crit << " "; // 输出元素值
    }
    std::cout << std::endl;
    return 0;
}
//这个例子中,我们使用了正向迭代器进行插入和删除操作,然后使用范围for循环遍历并输出vector容器的内容。
#include <iostream>
#include <vector>
int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    // 使用正向迭代器在第3个位置插入元素10
    vec.insert(vec.begin() + 2, 10);
    // 使用正向迭代器删除第2个元素
    vec.erase(vec.begin() + 1);
    // 输出结果
    for (const auto& element : vec) {
        std::cout << element << " ";
    }
    std::cout << std::endl;
    return 0;
}

Vector容器的元素访问

在使用vector容器时,我们需要对容器中的元素进行访问。有多种方法可以访问vector容器的元素

使用下标操作符

我们可以使用下标操作符[]访问vector中的元素。下标从0开始,与数组类似。

std::vector<int> vec = {1, 2, 3, 4, 5};
int value = vec[2]; // 获取第3个元素(下标为2),value = 3

需要注意的是,下标操作符[]不进行越界检查。如果下标超过了vector的大小,将导致未定义行为。

使用成员函数:at(), front(), back()

为了更安全地访问vector元素,可以使用以下成员函数:

  1. at(index):访问给定索引处的元素。与下标操作符类似,但会检查索引是否越界。如果越界,会抛出一个std::out_of_range异常。
std::vector<int> vec = {1, 2, 3, 4, 5};
int value = vec.at(2); // 获取第3个元素(下标为2),value = 3
  1. front():访问vector中的第一个元素。
std::vector<int> vec = {1, 2, 3, 4, 5};
int first = vec.front(); // 获取第一个元素,first = 1
  1. back():访问vector中的最后一个元素。
std::vector<int> vec = {1, 2, 3, 4, 5};
int last = vec.back(); // 获取最后一个元素,last = 5

容器越界访问的问题与解决方案

C++ vector 容器越界访问的问题是指在使用 std::vector 时,访问下标超出了 vector 的实际长度,这会导致未定义的行为(undefined behavior),可能引发程序崩溃或产生其他问题。为了避免这些问题,我们需要确保在访问 vector 元素时,不要越界访问。下面我们详细介绍一下越界访问的问题和解决方案。

问题:

  1. 使用 [] 操作符访问越界的下标:使用 [] 操作符访问 vector 时,不会进行边界检查,这可能导致访问越界。
  2. 使用 at() 函数访问越界的下标:at() 函数会进行边界检查,如果访问越界,将抛出 std::out_of_range 异常。
  3. 使用迭代器遍历 vector 时越界:在使用迭代器遍历 vector 时,如果越界,可能导致程序崩溃。

解决方案:

  1. 使用 at() 函数:尽量使用 at() 函数代替 [] 操作符访问 vector 元素,因为 at() 函数会进行边界检查。当访问越界时,at() 函数会抛出 std::out_of_range 异常,我们可以捕获这个异常,从而避免未定义行为。
#include <iostream>
#include <vector>
#include <stdexcept>
int main() {
  std::vector<int> v = {1, 2, 3, 4, 5};
  try {
    int value = v.at(10); // 越界访问
  } catch (const std::out_of_range& e) {
    std::cerr << "Out of range error: " << e.what() << std::endl;
  }
  return 0;
}
  1. 检查下标:在使用 [] 操作符访问 vector 元素之前,先检查下标是否越界。
std::vector<int> v = {1, 2, 3, 4, 5};
size_t index = 10;
if (index >= 0 && index < v.size()) {
  int value = v[index];
} else {
  std::cerr << "Index out of range." << std::endl;
}
  1. 使用范围 for 循环或迭代器:在遍历 vector 时,使用范围 for 循环或正确的迭代器边界。
std::vector<int> v = {1, 2, 3, 4, 5};
//for (auto value  = v.begin(); value != v.end(); ++value )
for (int value : v) {
  std::cout << value << std::endl;
}

Vector容器的插入与删除操作

std::vector是C++标准库中的一个动态数组容器,提供了一种灵活的数据结构,用于存储和操作同一类型的元素。Vector容器主要提供了以下插入与删除操作的功能:

插入元素:push_back(), insert()

  1. push_back(): 向vector的尾部添加一个元素,自动调整容量。时间复杂度通常是O(1),但如果发生扩容,则是O(n)。
std::vector<int> vec;
vec.push_back(42);
  1. insert(): 在指定位置插入元素,如果插入位置不是vector的尾部,则需要移动插入位置之后的所有元素。时间复杂度是O(n)。
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin() + 1;
vec.insert(it, 42);  // 在位置1插入42,vec变为 {1, 42, 2, 3}
  • 删除元素:pop_back(), erase(), clear()
  1. pop_back(): 删除vector尾部的元素,自动调整容量。时间复杂度是O(1)。
std::vector<int> vec = {1, 2, 3};
vec.pop_back();  // vec变为 {1, 2}
  1. erase(): 删除指定位置或区间的元素,可能需要移动删除位置之后的所有元素。时间复杂度是O(n)。
std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin() + 2;
vec.erase(it);  // 删除位置2的元素,vec变为 {1, 2, 4, 5}
  1. clear(): 删除vector中的所有元素,容量保持不变。时间复杂度是O(n)。
std::vector<int> vec = {1, 2, 3};
vec.clear();  // vec变为空,但容量保持不变

元素插入与删除的性能分析

  1. push_back()pop_back():因为它们只在vector的尾部进行操作,通常具有较高的性能,时间复杂度为O(1)。但push_back()在需要扩容时,时间复杂度会升至O(n)。
  2. insert()erase():它们在指定位置进行操作,可能需要移动元素。因此,时间复杂度是O(n),性能较低。在操作vector时,应尽量避免在中间位置频繁插入或删除元素,以减少性能损失。
  3. clear():虽然时间复杂度是O(n),但它一次性删除所有元素,相对高效。

Vector容器的容量与大小

vector容器的大小和容量是两个重要的概念,了解它们之间的区别和如何调整它们对于高效使用vector非常重要。

size()与capacity()的区别

  • size()vector容器的大小是指它所包含的元素的数量。我们可以通过size()成员函数获得当前vector的大小。
  • capacity()vector容器的容量是指它可以容纳的最大元素数量,而不需要重新分配内存。我们可以通过capacity()成员函数来获得当前vector的容量。

当我们向vector中添加元素时,它会自动检查当前容量是否足够。如果不够,则会重新分配更大的内存空间,并将原来的元素复制到新的内存区域。这个过程可能导致性能损失。

使用reserve()与resize()调整容量与大小

  • reserve():此成员函数可以用来预先为vector分配一定的内存空间。当我们知道vector的最大大小时,可以通过reserve()函数预先分配足够的内存空间,以避免频繁的内存重新分配。需要注意的是,reserve()只改变容器的容量,而不改变它的大小。
  • resize():此成员函数可以用来改变vector容器的大小。我们可以传递一个新的大小作为参数,这样vector会根据需要增大或缩小。如果增大,则新添加的元素会默认初始化;如果缩小,则多余的元素将被丢弃。需要注意的是,resize()会同时改变容器的大小和容量。

shrink_to_fit()的作用与使用场景

shrink_to_fit()是一个用于减小vector容量的成员函数,它可以将容量调整为与当前大小相等。这个函数主要用于释放多余的内存空间,以节省内存资源。一般在以下场景中使用:

  1. 当我们知道vector不再需要添加新元素时,可以调用shrink_to_fit()释放多余的内存空间。
  2. 当我们完成了一些vector的操作,导致大量元素被移除,同时确信未来不需要额外的容量时,可以调用shrink_to_fit()来释放内存空间。

需要注意的是,shrink_to_fit()并不保证一定会释放内存,这取决于具体的实现。为了获得更可靠的内存释放,可以考虑使用swap().

Vector容器与C++11/14/17新特性

列表初始化

在C++11中,引入了列表初始化(又称为统一初始化或初始化列表)。它允许我们在创建对象时,使用花括号 {} 初始化容器,如 std::vector。列表初始化在C++11中具有广泛的应用,提高了代码的可读性和简洁性。以下是一个使用列表初始化构造 std::vector 的示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    for (const auto &elem : vec) {
        std::cout << elem << ' ';
    }
    std::cout << std::endl;
    return 0;
}

使用emplace_back()与emplace()高效构造元素

C++11引入了emplace_back()和emplace()方法,以更高效地在容器中插入元素。这些方法可以在插入新元素时直接在容器内存中构造元素,从而避免了临时对象的创建与拷贝。以下是一个使用 emplace_back 和 emplace 方法的示例:

#include <iostream>
#include <vector>
class MyClass {
public:
    MyClass(int x, double y) : x(x), y(y) {}
    int getX() const { return x; }
    double getY() const { return y; }
private:
    int x;
    double y;
};
int main() {
    std::vector<MyClass> vec;
    // 使用 emplace_back 在末尾构造一个新元素
    vec.emplace_back(1, 1.5);
    vec.emplace_back(2, 2.5);
    // 使用 emplace 在指定位置构造一个新元素
    vec.emplace(vec.begin() + 1, 3, 3.5);
    for (const auto &elem : vec) {
        std::cout << elem.getX() << ", " << elem.getY() << std::endl;
    }
    return 0;
}

C++17中的if constexpr

C++17中的if constexpr 在C++17中引入了if constexpr,它允许编译期间根据条件选择不同的代码分支执行。当条件为true时,只有true分支的代码被实例化,false分支的代码将被忽略。这有助于减少不必要的编译时计算,并提高代码的可读性。以下是一个使用if constexpr的简单示例:

#include <iostream>
#include <type_traits>
template <typename T>
void print(const T &value) {
    if constexpr (std::is_integral<T>::value) {
        std::cout << "Integral value: " << value << std::endl;
    } else {
        std::cout << "Non-integral value: " << value << std::endl;
    }
}
int main() {
    int intValue = 42;
    double doubleValue = 42.0;
    print(intValue); // 输出
     return 0;
 }

Vector容器的性能优化技巧

尽量避免频繁扩容

Vector容器的大小是动态调整的,当容器中的元素数量超过其当前容量时,Vector会重新分配更大的内存空间,然后将旧数据拷贝到新的内存空间。这个过程称为扩容。频繁的扩容操作会导致额外的内存分配和拷贝开销,从而降低程序性能。为了减少扩容的次数,可以采用以下方法:

  • 在创建Vector对象时,如果已知大致的元素数量,可以使用reserve()函数预分配足够的内存空间。这将避免多次扩容操作。
  • 如果可能,尽量使用emplace_back()而非push_back()方法添加元素。emplace_back()在原地构造新元素,从而减少临时对象的创建和销毁开销。

使用std::move()避免不必要的拷贝

Vector在插入、删除和扩容时,需要对元素进行拷贝操作。这在处理大型对象时可能会导致性能下降。为了减少拷贝操作,可以使用std::move()将对象从一个容器移动到另一个容器,从而避免创建临时副本。这在处理大型对象和自定义类型时尤为有用。

利用空间换时间的策略

空间换时间是一种优化策略,通过使用额外的内存空间来减少计算时间。在Vector容器的性能优化中,可以采用以下方法:

  • 当Vector的大小不断变化时,可以适当预留一些额外的内存空间,以减少内存分配和拷贝操作。这可以通过使用reserve()函数来实现。
  • 在合适的情况下,可以使用shrink_to_fit()函数来释放未使用的内存空间。这可以帮助节省内存,但可能导致重新分配内存的开销增加。因此,在实际使用中,需要根据具体情况权衡利弊。

Vector容器实战案例分析

实现简单的三维数组

#include <iostream>
#include <vector>
int main() {
    // 定义三维数组的维度大小
    const size_t dim1 = 3;
    const size_t dim2 = 4;
    const size_t dim3 = 5;
    // 创建一个3维的char *数组(使用嵌套的Vector容器)
    std::vector<std::vector<std::vector<char *>>> array3D(dim1,
        std::vector<std::vector<char *>>(dim2,
            std::vector<char *>(dim3, nullptr)));
    // 初始化三维数组的所有元素为空字符串
    for (size_t i = 0; i < dim1; ++i) {
        for (size_t j = 0; j < dim2; ++j) {
            for (size_t k = 0; k < dim3; ++k) {
                array3D[i][j][k] = new char[1];
                array3D[i][j][k][0] = '\0';
            }
        }
    }
    // 使用三维数组
    // ...
    // 释放动态分配的内存
    for (size_t i = 0; i < dim1; ++i) {
        for (size_t j = 0; j < dim2; ++j) {
            for (size_t k = 0; k < dim3; ++k) {
                delete[] array3D[i][j][k];
            }
        }
    }
    return 0;
}

使用Vector容器构建自定义字符串类

下面的示例展示了如何使用std::vector容器构建一个简单的自定义字符串类。这个自定义字符串类将内部存储封装在一个std::vector中,同时提供了一些基本的字符串操作方法。

#include <iostream>
#include <vector>
#include <stdexcept>
class CustomString {
public:
    // 默认构造函数
    CustomString() {
        data_.push_back('\0');
    }
    // 从const char *构造
    CustomString(const char *str) {
        if (str == nullptr) {
            throw std::invalid_argument("Invalid input string.");
        }
        while (*str) {
            data_.push_back(*str++);
        }
        data_.push_back('\0');
    }
    // 拷贝构造函数
    CustomString(const CustomString &other) : data_(other.data_) {}
    // 移动构造函数
    CustomString(CustomString &&other) noexcept : data_(std::move(other.data_)) {}
    // 拷贝赋值运算符
    CustomString &operator=(const CustomString &other) {
        if (this == &other) {
            return *this;
        }
        data_ = other.data_;
        return *this;
    }
    // 移动赋值运算符
    CustomString &operator=(CustomString &&other) noexcept {
        if (this == &other) {
            return *this;
        }
        data_ = std::move(other.data_);
        return *this;
    }
    // 返回字符串长度(不包括空字符)
    size_t length() const {
        return data_.size() - 1;
    }
    // 重载[]运算符以访问字符串中的字符
    char &operator[](size_t index) {
        if (index >= length()) {
            throw std::out_of_range("Index out of range.");
        }
        return data_[index];
    }
    const char &operator[](size_t index) const {
        if (index >= length()) {
            throw std::out_of_range("Index out of range.");
        }
        return data_[index];
    }
    // 返回C风格字符串
    const char *c_str() const {
        return data_.data();
    }
private:
    std::vector<char> data_;
};
int main() {
    CustomString str("Hello, CustomString!");
    std::cout << "Length: " << str.length() << std::endl;
    std::cout << "Content: " << str.c_str() << std::endl;
    CustomString copy_str(str);
    std::cout << "Copy Content: " << copy_str.c_str() << std::endl;
    CustomString move_str(std::move(copy_str));
    std::cout << "Move Content: " << move_str.c_str() << std::endl;
    return 0;
}

tcp服务器基于Vector容器的内存池的实现

这个示例展示了如何在Linux环境下,使用std::vector容器构建一个简单的内存池,以及如何利用多线程和select()函数处理TCP客户端。在这个示例中,我们将创建一个简单的TCP服务器,使用select()接收客户端连接并分配到一个线程池中进行处理,

//这个示例完成了TCP服务器的实现,使用select函数监听套接字,同时使用内存池和线程池来高效处理客户端连接。
//当新的客户端连接到达时,服务器将连接分配给线程池处理,线程处理完连接后将内存归还给内存池。
//在本示例中,服务器将接收到的消息打印到控制台。
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
// 内存池和线程池参数
const int MEMORY_POOL_SIZE = 10;
const int THREAD_POOL_SIZE = 4;
// 内存池结构体
struct MemoryPool {
    std::vector<char *> memory_pool;
    std::mutex mtx;
    std::condition_variable cv;
    MemoryPool(int size) {
        for (int i = 0; i < size; ++i) {
            memory_pool.push_back(new char[1024]);
        }
    }
    ~MemoryPool() {
        for (auto &ptr : memory_pool) {
            delete[] ptr;
        }
    }
    char *allocate() {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [this]() { return !memory_pool.empty(); });
        char *buffer = memory_pool.back();
        memory_pool.pop_back();
        return buffer;
    }
    void deallocate(char *buffer) {
        std::unique_lock<std::mutex> lock(mtx);
        memory_pool.push_back(buffer);
        cv.notify_one();
    }
};
// 线程池任务队列
std::queue<int> task_queue;
std::mutex queue_mtx;
std::condition_variable queue_cv;
// 线程处理函数
void thread_handler(MemoryPool &memory_pool) {
    while (true) {
        int client_fd;
        {
            std::unique_lock<std::mutex> lock(queue_mtx);
            queue_cv.wait(lock, []() { return !task_queue.empty(); });
            client_fd = task_queue.front();
            task_queue.pop();
        }
        char *buffer = memory_pool.allocate();
        ssize_t received = recv(client_fd, buffer, 1024, 0);
        if (received > 0) {
            // 处理接收到的数据
            // ...
            std::cout << "Received: " << buffer << std::endl;
        }
        memory_pool.deallocate(buffer);
        close(client_fd);
    }
}
int main() {
    int server_fd, client_fd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t addr_size = sizeof(struct sockaddr_in);
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        std::cerr << "Failed to create socket." << std::endl;
        return -1;
    }
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    server_addr.sin_addr.s_addr = INADDR_ANY;
    if (bind(server_fd, (struct sockaddr *)&server_addr, addr_size) < 0) {
        std::cerr << "Failed to bind socket." << std::endl;
        close(server_fd);
        return -1;
    }
    if (listen(server_fd, 10) < 0) {
        std::cerr << "Failed to listen on socket." << std::endl;
        close(server_fd);
        return -1;
    }
    MemoryPool memory_pool(MEMORY_POOL_SIZE);
    std::vector<std::thread> thread_pool;
    for (int i = 0; i < THREAD_POOL_SIZE; ++i) {
        thread_pool.emplace_back(thread_handler, std::ref(memory_pool));
    }
    fd_set read_fds;
    int max_fd = server_fd;
    while (true) {
        FD_ZERO(&read_fds);
        FD_SET(server_fd, &read_fds);
        timeval timeout;
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        int activity = select(max_fd + 1, &read_fds, NULL, NULL, &timeout);
        if (activity < 0) {
            std::cerr << "Select error." << std::endl;
            continue;
        }
        if (FD_ISSET(server_fd, &read_fds)) {
            client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_size);
            if (client_fd < 0) {
                std::cerr << "Failed to accept client." << std::endl;
                continue;
            }
            std::unique_lock<std::mutex> lock(queue_mtx);
            task_queue.push(client_fd);
            queue_cv.notify_one();
        }
    }
    // 销毁线程池
    for (auto &t : thread_pool) {
        t.join();
    }
    close(server_fd);
    return 0;
}

总结与展望

Vector容器在C++编程中的优势与挑战

优势

  1. 动态数组:Vector容器提供了动态数组的功能,可以在运行时调整大小,方便程序员处理不定长数据。
  2. 随机访问:Vector容器支持随机访问,即使用下标索引访问元素,这使得访问速度快,时间复杂度为O(1)。
  3. 内存连续:Vector容器内部的元素在内存中是连续存储的,这有助于提高程序性能,尤其是在缓存方面。
  4. 兼容C风格数组:由于Vector容器的内存连续性,它可以很容易地与C风格数组互操作。

挑战

  1. 插入与删除开销:Vector容器在非尾部进行插入和删除操作时,可能需要移动大量元素,导致时间复杂度为O(n)。
  2. 容量与大小管理:虽然Vector容器会根据需要自动调整容量,但过多的调整会导致额外的开销。程序员需要合理管理Vector的容量与大小,以免浪费资源。

C++标准模板库中其他容器的简介与比较

  1. List(双向链表):List容器允许在O(1)时间内进行插入和删除操作,但不支持随机访问。由于非连续内存存储,它对缓存的利用较低。
  2. Deque(双端队列):Deque容器提供了两端高效的插入和删除操作,并支持随机访问。它在内存中的存储结构是分段连续的。
  3. Set/Map(基于红黑树实现):Set和Map容器提供了O(log n)时间内的查找、插入和删除操作。Set容器用于存储无重复元素的集合,而Map容器用于存储键值对。
  4. Unordered_set/Unordered_map(基于哈希表实现):Unordered_set和Unordered_map容器通常提供了平均O(1)时间复杂度的查找、插入和删除操作。它们在性能方面往往优于Set和Map,但不保证元素的有序性。

提高Vector容器编程技巧与应用的建议

  1. 合理预分配内存:在已知Vector容器大小的情况下,使用reserve()函数预分配内存,可以避免不必要的内存重新分配和数据移动开销。
  2. 利用迭代器:使用Vector容器提供的迭代器进行遍历,可以提高代码可读性和可维护性。
目录
相关文章
|
3天前
|
C++ 索引 容器
黑马c++ STL部分 笔记(9) map/multimap容器
黑马c++ STL部分 笔记(9) map/multimap容器
|
3天前
|
C++ 容器
黑马c++ STL部分 笔记(8) set/ multiset 容器
黑马c++ STL部分 笔记(8) set/ multiset 容器
|
3天前
|
存储 C++ 容器
黑马c++ STL部分 笔记(7) list容器
黑马c++ STL部分 笔记(7) list容器
|
3天前
|
C++ 容器
黑马c++ STL部分 笔记(6) queue 容器
黑马c++ STL部分 笔记(6) queue 容器
|
3天前
|
C++ 容器
黑马c++ STL部分 笔记(5) stack容器
黑马c++ STL部分 笔记(5) stack容器
|
3天前
|
C++ 容器
黑马c++ STL部分 笔记(3) deque容器
黑马c++ STL部分 笔记(3) deque容器
|
3天前
|
C++ 容器
黑马c++ STL部分 笔记(3) vector容器
黑马c++ STL部分 笔记(3) vector容器
|
3天前
|
C++ 容器
黑马c++ STL部分 笔记(2) string容器
黑马c++ STL部分 笔记(2) string容器
|
4天前
|
C++
C++派生类
C++派生类
14 0
|
4天前
|
存储 程序员 数据安全/隐私保护
C++类
C++类
14 0