引言
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会自动扩容,通常以当前容量的两倍进行扩展。
常用成员函数及其作用
- 构造函数:
vector v;
:创建一个空的vector对象,元素类型为T。vector v(n);
:创建一个具有n个元素的vector对象,元素类型为T。vector v(n, value);
:创建一个具有n个元素的vector对象,元素类型为T,所有元素初始值为value。
assign()
:为vector分配新的元素并替换当前的元素。
v.assign(n, value);
:将vector的大小调整为n,并将所有元素设置为value。v.assign(first, last);
:用迭代器范围[first, last)中的元素替换当前vector的内容。
push_back()
:在vector的末尾添加一个元素。
v.push_back(value);
:将value添加到vector的末尾。
pop_back()
:删除vector末尾的元素。
v.pop_back();
:删除vector末尾的元素。
insert()
:在指定位置插入元素。
v.insert(pos, value);
:在pos位置插入一个value元素。v.insert(pos, n, value);
:在pos位置插入n个value元素。v.insert(pos, first, last);
:在pos位置插入迭代器范围[first, last)内的元素。
erase()
:删除指定位置的元素或一定范围内的元素。
v.erase(pos);
:删除pos位置的元素。v.erase(first, last);
:删除迭代器范围[first, last)内的元素。
clear()
:清除vector中的所有元素。
v.clear();
:清空vector。
size()
:返回vector中元素的个数。
v.size();
:获取vector的大小。
empty()
:检查vector是否为空。
v.empty();
:若vector为空,返回true;否则返回false。
resize()
:改变vector的大小。
v.resize(n);
:将vector的大小调整为n。v.resize(n, value);
:将vector的大小调整为n,新增的元素初始值为value。
capacity()
:返回vector的容量。
v.capacity();
:获取vector的容量。
reserve()
:改变vector的容量。
v.reserve(n);
:将vector的容量调整至至少为n,如果当前容量大于或等于n,则不改变。
shrink_to_fit()
:将vector的容量收缩至与其大小相同。
v.shrink_to_fit();
:收缩vector的容量。
- 访问元素:
v.at(index);
:返回位于给定索引位置的元素的引用。如果索引越界,则抛出一个out_of_range
异常。v[index];
:返回位于给定索引位置的元素的引用。但是不进行越界检查。v.front();
:返回vector首元素的引用。v.back();
:返回vector末尾元素的引用。
- 迭代器相关:
v.begin();
:返回指向vector第一个元素的迭代器。v.end();
:返回指向vector最后一个元素之后的迭代器。v.rbegin();
:返回指向vector最后一个元素的反向迭代器。v.rend();
:返回指向vector第一个元素之前的反向迭代器。v.cbegin();
:返回指向vector第一个元素的常量迭代器。v.cend();
:返回指向vector最后一个元素之后的常量迭代器。v.crbegin();
:返回指向vector最后一个元素的常量反向迭代器。v.crend();
:返回指向vector第一个元素之前的常量反向迭代器。
swap()
:交换两个vector对象的内容。
v1.swap(v2);
:将v1和v2的内容互换。
emplace()
:在指定位置构造一个新元素。
v.emplace(pos, args);
:在pos位置使用args参数构造一个新元素。
emplace_back()
:在vector末尾构造一个新元素。
v.emplace_back(args);
:在vector末尾使用args参数构造一个新元素。
max_size()
:返回vector在内存中可能容纳的最大元素数量。
v.max_size();
:获取vector的最大容量。
- 比较操作符:
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)是一种用于访问和遍历容器中元素的对象。它可以将访问的复杂性隐藏在底层实现中,提供了类似于指针的访问和操作功能。
迭代器的概念与分类
迭代器根据访问和操作的权限,可以分为以下几类:
- 输入迭代器(Input Iterator):用于读取顺序访问的元素。
- 输出迭代器(Output Iterator):用于写入顺序访问的元素。
- 前向迭代器(Forward Iterator):支持输入和输出迭代器的功能,还可以多次遍历。
- 双向迭代器(Bidirectional Iterator):支持前向迭代器的功能,还可以向前和向后遍历。
- 随机访问迭代器(Random Access Iterator):支持双向迭代器的功能,还可以进行随机访问。
Vector容器的迭代器类型
Vector容器支持以下迭代器类型:
- 正向迭代器(iterator):双向迭代器,可以向前和向后遍历元素。
- 反向迭代器(reverse_iterator):双向迭代器,从后向前遍历元素。
- 常量正向迭代器(const_iterator):只读迭代器,不能修改元素值。
- 常量反向迭代器(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元素,可以使用以下成员函数:
at(index)
:访问给定索引处的元素。与下标操作符类似,但会检查索引是否越界。如果越界,会抛出一个std::out_of_range
异常。
std::vector<int> vec = {1, 2, 3, 4, 5}; int value = vec.at(2); // 获取第3个元素(下标为2),value = 3
front()
:访问vector中的第一个元素。
std::vector<int> vec = {1, 2, 3, 4, 5}; int first = vec.front(); // 获取第一个元素,first = 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 元素时,不要越界访问。下面我们详细介绍一下越界访问的问题和解决方案。
问题:
- 使用 [] 操作符访问越界的下标:使用 [] 操作符访问 vector 时,不会进行边界检查,这可能导致访问越界。
- 使用 at() 函数访问越界的下标:at() 函数会进行边界检查,如果访问越界,将抛出 std::out_of_range 异常。
- 使用迭代器遍历 vector 时越界:在使用迭代器遍历 vector 时,如果越界,可能导致程序崩溃。
解决方案:
- 使用 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; }
- 检查下标:在使用 [] 操作符访问 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; }
- 使用范围 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()
push_back()
: 向vector的尾部添加一个元素,自动调整容量。时间复杂度通常是O(1),但如果发生扩容,则是O(n)。
std::vector<int> vec; vec.push_back(42);
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()
pop_back()
: 删除vector尾部的元素,自动调整容量。时间复杂度是O(1)。
std::vector<int> vec = {1, 2, 3}; vec.pop_back(); // vec变为 {1, 2}
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}
clear()
: 删除vector中的所有元素,容量保持不变。时间复杂度是O(n)。
std::vector<int> vec = {1, 2, 3}; vec.clear(); // vec变为空,但容量保持不变
元素插入与删除的性能分析
push_back()
和pop_back()
:因为它们只在vector的尾部进行操作,通常具有较高的性能,时间复杂度为O(1)。但push_back()
在需要扩容时,时间复杂度会升至O(n)。insert()
和erase()
:它们在指定位置进行操作,可能需要移动元素。因此,时间复杂度是O(n),性能较低。在操作vector时,应尽量避免在中间位置频繁插入或删除元素,以减少性能损失。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
容量的成员函数,它可以将容量调整为与当前大小相等。这个函数主要用于释放多余的内存空间,以节省内存资源。一般在以下场景中使用:
- 当我们知道
vector
不再需要添加新元素时,可以调用shrink_to_fit()
释放多余的内存空间。 - 当我们完成了一些
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++编程中的优势与挑战
优势
- 动态数组:Vector容器提供了动态数组的功能,可以在运行时调整大小,方便程序员处理不定长数据。
- 随机访问:Vector容器支持随机访问,即使用下标索引访问元素,这使得访问速度快,时间复杂度为O(1)。
- 内存连续:Vector容器内部的元素在内存中是连续存储的,这有助于提高程序性能,尤其是在缓存方面。
- 兼容C风格数组:由于Vector容器的内存连续性,它可以很容易地与C风格数组互操作。
挑战
- 插入与删除开销:Vector容器在非尾部进行插入和删除操作时,可能需要移动大量元素,导致时间复杂度为O(n)。
- 容量与大小管理:虽然Vector容器会根据需要自动调整容量,但过多的调整会导致额外的开销。程序员需要合理管理Vector的容量与大小,以免浪费资源。
C++标准模板库中其他容器的简介与比较
- List(双向链表):List容器允许在O(1)时间内进行插入和删除操作,但不支持随机访问。由于非连续内存存储,它对缓存的利用较低。
- Deque(双端队列):Deque容器提供了两端高效的插入和删除操作,并支持随机访问。它在内存中的存储结构是分段连续的。
- Set/Map(基于红黑树实现):Set和Map容器提供了O(log n)时间内的查找、插入和删除操作。Set容器用于存储无重复元素的集合,而Map容器用于存储键值对。
- Unordered_set/Unordered_map(基于哈希表实现):Unordered_set和Unordered_map容器通常提供了平均O(1)时间复杂度的查找、插入和删除操作。它们在性能方面往往优于Set和Map,但不保证元素的有序性。
提高Vector容器编程技巧与应用的建议
- 合理预分配内存:在已知Vector容器大小的情况下,使用
reserve()
函数预分配内存,可以避免不必要的内存重新分配和数据移动开销。 - 利用迭代器:使用Vector容器提供的迭代器进行遍历,可以提高代码可读性和可维护性。