第9章 数组和指针 数组 数组的基本概念 数组是一种数据结构,用于存储相同类型的多个元素。在C++中,数组的大小在声明时必须确定,且在程序运行过程中不能改变。数组的核心特性是连续内存布局 和固定大小 ,这使得数组具有高效的随机访问性能。
数组的底层实现 数组在内存中以连续的方式存储元素,这意味着:
内存连续性 :数组元素在内存中是连续存储的,相邻元素之间没有间隙元素访问 :通过基地址加上偏移量计算元素地址,实现O(1)时间复杂度的随机访问内存布局 :数组的内存布局由元素类型和数组大小决定1 2 3 4 5 6 7 8 9 int arr[5 ] = {10 , 20 , 30 , 40 , 50 };
数组的内存对齐与硬件映射 数组元素的内存对齐对于性能至关重要,它直接影响CPU缓存命中率和内存访问效率:
1 2 3 4 5 6 7 8 9 10 11 struct alignas (16 ) AlignedType { int x; double y; }; AlignedType arr[2 ];
内存对齐的底层原理 硬件约束 :现代CPU对内存访问有对齐要求,未对齐的访问会导致性能下降甚至硬件异常缓存行优化 :对齐的数据能更好地利用CPU缓存行(通常为64字节)SIMD指令 :SIMD指令要求数据必须对齐到特定边界(如16字节或32字节)1 2 3 4 5 6 struct alignas (64 ) CacheLineAligned { double data[8 ]; }; CacheLineAligned arr[4 ];
汇编级内存对齐分析 1 2 3 4 5 6 7 8 9 ; x86-64汇编示例:对齐内存访问 mov rax, arr ; 加载数组基地址 movaps xmm0, [rax] ; 对齐的SIMD加载(要求16字节对齐) movups xmm1, [rax+8] ; 未对齐的SIMD加载(性能较慢) ; 数组元素访问的地址计算 ; arr[i] 的地址计算 lea rdx, [rax + rdi*8] ; rdi = i, 8 = sizeof(double) movsd xmm0, [rdx] ; 加载double元素
内存对齐优化策略 使用alignas指定对齐要求 :显式控制内存对齐避免跨越缓存行 :设计数据结构时考虑缓存行边界内存分配对齐 :使用aligned_alloc或std::aligned_alloc分配对齐内存编译选项优化 :使用-falign-arrays等编译器选项1 2 3 4 5 6 7 void * alignedMem = std::aligned_alloc (64 , 1024 );if (alignedMem) { double * arr = static_cast <double *>(alignedMem); std::free (alignedMem); }
数组的类型特性 数组在C++中具有独特的类型特性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int arr[5 ];static_assert (std::is_same_v<decltype (arr), int [5 ]>);const int * ptr = arr; int (&arrRef)[5 ] = arr;static_assert (std::is_same_v<decltype (arrRef), int (&)[5 ]>);constexpr size_t size = sizeof (arr) / sizeof (arr[0 ]);static_assert (size == 5 );template <typename T>void printType () { std::cout << typeid (T).name () << std::endl; } printType <decltype (arr)>();
数组的声明和初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> int main () { int numbers[5 ]; numbers[0 ] = 10 ; numbers[1 ] = 20 ; numbers[2 ] = 30 ; numbers[3 ] = 40 ; numbers[4 ] = 50 ; int scores[3 ] = {95 , 87 , 91 }; double prices[] = {19.99 , 29.99 , 39.99 , 49.99 }; char message[] = "Hello, World!" ; std::cout << "Numbers: " ; for (int i = 0 ; i < 5 ; i++) { std::cout << numbers[i] << " " ; } std::cout << std::endl; std::cout << "Scores: " ; for (int score : scores) { std::cout << score << " " ; } std::cout << std::endl; return 0 ; }
数组的特性 连续存储 :数组元素在内存中是连续存储的固定大小 :数组大小在声明时确定,不能动态改变索引访问 :使用下标(从0开始)访问数组元素边界检查 :C++不自动检查数组边界,需要程序员自己确保不越界多维数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> int main () { int matrix[2 ][3 ] = { {1 , 2 , 3 }, {4 , 5 , 6 } }; std::cout << "matrix[0][0]: " << matrix[0 ][0 ] << std::endl; std::cout << "matrix[1][2]: " << matrix[1 ][2 ] << std::endl; std::cout << "Matrix: " << std::endl; for (int i = 0 ; i < 2 ; i++) { for (int j = 0 ; j < 3 ; j++) { std::cout << matrix[i][j] << " " ; } std::cout << std::endl; } int cube[2 ][2 ][2 ] = { {{1 , 2 }, {3 , 4 }}, {{5 , 6 }, {7 , 8 }} }; return 0 ; }
指针 指针的基本概念 指针是一种变量,用于存储内存地址。通过指针,可以间接访问和修改内存中的数据。指针的核心价值在于提供了直接内存操作能力 和高效的参数传递方式 。
指针的底层实现 指针在内存中存储的是另一个变量的内存地址:
指针大小 :指针的大小取决于目标平台的地址空间大小(32位系统为4字节,64位系统为8字节)指针类型 :指针的类型决定了指针算术的步长和对指向内存的解释方式内存布局 :指针变量本身在内存中占用固定大小的空间,存储的是目标变量的地址1 2 3 4 5 6 7 8 9 10 11 12 int x = 100 ; int * ptr = &x; std::cout << "Size of int*: " << sizeof (int *) << " bytes" << std::endl; std::cout << "Size of double*: " << sizeof (double *) << " bytes" << std::endl; std::cout << "Size of void*: " << sizeof (void *) << " bytes" << std::endl;
指针类型的重要性 指针的类型决定了:
解引用的行为 :如何解释指向的内存内容指针算术的步长 :指针加减操作的字节数类型安全 :不同类型的指针之间的转换需要显式进行1 2 3 4 5 6 7 8 9 10 11 12 int arr[5 ] = {1 , 2 , 3 , 4 , 5 };int * intPtr = arr;double * doublePtr = reinterpret_cast <double *>(arr);std::cout << "intPtr: " << intPtr << ", *intPtr: " << *intPtr << std::endl; intPtr++; std::cout << "intPtr++: " << intPtr << ", *intPtr: " << *intPtr << std::endl; std::cout << "doublePtr: " << doublePtr << ", *doublePtr: " << *doublePtr << std::endl; doublePtr++; std::cout << "doublePtr++: " << doublePtr << ", *doublePtr: " << *doublePtr << std::endl;
指针的内存对齐 指针的对齐要求取决于其指向的数据类型:
1 2 3 4 5 6 7 8 9 10 11 12 struct alignas (16 ) AlignedData { int x; double y; }; AlignedData data; AlignedData* ptr = &data; std::cout << "Address of data: " << &data << std::endl; std::cout << "Alignment requirement of AlignedData: " << alignof (AlignedData) << " bytes" << std::endl; std::cout << "Is ptr aligned? " << ((reinterpret_cast <uintptr_t >(ptr) % alignof (AlignedData)) == 0 ) << std::endl;
指针的声明和初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> int main () { int x = 100 ; int * ptr = &x; std::cout << "x = " << x << std::endl; std::cout << "&x = " << &x << std::endl; std::cout << "ptr = " << ptr << std::endl; std::cout << "*ptr = " << *ptr << std::endl; *ptr = 200 ; std::cout << "After modification: " << std::endl; std::cout << "x = " << x << std::endl; std::cout << "*ptr = " << *ptr << std::endl; int * nullPtr = nullptr ; int * oldNullPtr = NULL ; return 0 ; }
指针的运算 指针算术是C++中强大的特性,它允许指针根据所指向类型的大小进行算术运算。指针算术的底层实现依赖于指针的类型信息,这使得指针能够以正确的步长在内存中移动,是实现高效数组操作和内存管理的基础。
指针算术的底层实现 指针算术的核心是按类型大小计算偏移量 ,由编译器在编译期完成地址计算:
指针加法 :ptr + n 等价于 reinterpret_cast<char*>(ptr) + n * sizeof(*ptr)指针减法 :ptr - n 等价于 reinterpret_cast<char*>(ptr) - n * sizeof(*ptr)指针比较 :基于指针在内存中的实际地址进行比较指针差值 :ptr2 - ptr1 计算元素个数,结果类型为 ptrdiff_t1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int arr[5 ] = {10 , 20 , 30 , 40 , 50 };int * ptr = arr;std::cout << "ptr = " << static_cast <void *>(ptr) << std::endl; ptr++; std::cout << "ptr++ = " << static_cast <void *>(ptr) << std::endl; ptr = arr + 4 ; std::cout << "ptr = " << static_cast <void *>(ptr) << std::endl; ptr--; std::cout << "ptr-- = " << static_cast <void *>(ptr) << std::endl; int * start = arr;int * end = arr + 5 ;ptrdiff_t elements = end - start;std::cout << "Elements between start and end: " << elements << std::endl;
汇编级指针算术分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ; x86-64汇编示例:指针算术 section .data arr dq 10, 20, 30, 40, 50 ; 64位整数数组 section .text global main main: ; 加载数组基地址 lea rax, [arr] ; 指针加法:ptr + 2 mov rdi, 2 lea rdx, [rax + rdi*8] ; 8 = sizeof(int64_t) ; 指针减法:ptr - 1 sub rdx, 8 ; 等价于 rdx-- ; 指针比较:ptr1 < ptr2 cmp rax, rdx jl less_than ; 指针差值计算 sub rdx, rax shr rdx, 3 ; 除以8得到元素个数 less_than: ret
指针算术的SIMD优化 现代编译器会利用SIMD指令优化指针算术操作,特别是在数组遍历和数值计算中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <immintrin.h> void vectorizedArrayAdd (const float * a, const float * b, float * result, size_t size) { size_t i = 0 ; for (; i + 15 < size; i += 16 ) { __m512 va = _mm512_loadu_ps(&a[i]); __m512 vb = _mm512_loadu_ps(&b[i]); __m512 vres = _mm512_add_ps(va, vb); _mm512_storeu_ps(&result[i], vres); } for (; i < size; i++) { result[i] = a[i] + b[i]; } }
指针算术的高级应用 内存块管理 :使用指针算术在动态分配的内存块中导航数据结构实现 :链表、树等数据结构的节点遍历字符串处理 :C风格字符串的高效操作类型 punning :通过指针类型转换实现不同类型间的转换内存对齐检查 :使用指针算术验证内存对齐1 2 3 4 5 6 7 8 9 10 11 12 template <typename T>bool isAligned (const T* ptr, size_t alignment) { return (reinterpret_cast <uintptr_t >(ptr) % alignment) == 0 ; } float intToFloat (int value) { int * iptr = &value; float * fptr = reinterpret_cast <float *>(iptr); return *fptr; }
指针算术的性能考量 编译期优化 :编译器会将指针算术转换为高效的地址计算缓存局部性 :顺序指针访问具有良好的缓存局部性分支预测 :线性指针算术有助于分支预测SIMD友好 :连续的指针访问适合SIMD向量化边界检查 :指针算术需要手动确保不越界1 2 3 4 5 6 7 8 9 10 11 12 void processArray (const int * arr, size_t size) { for (const int * p = arr; p < arr + size; p++) { } for (size_t i = 0 ; i < size; i += 100 ) { } }
高级指针操作 1. 指针与数组的关系深入 数组名在大多数情况下会衰减为指向第一个元素的指针,但有一些例外情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int arr[5 ] = {1 , 2 , 3 , 4 , 5 };std::cout << "sizeof(arr): " << sizeof (arr) << std::endl; int (*ptrToArr)[5 ] = &arr; std::cout << "*ptrToArr: " << *ptrToArr << std::endl; std::cout << "(**ptrToArr): " << **ptrToArr << std::endl; int (&arrRef)[5 ] = arr; template <typename T>void foo (T t) { std::cout << typeid (T).name () << std::endl; } foo (arr);
2. 指向多维数组的指针 多维数组的指针操作需要理解数组的内存布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int matrix[2 ][3 ] = {{1 , 2 , 3 }, {4 , 5 , 6 }};int * elemPtr = &matrix[0 ][0 ];int (*rowPtr)[3 ] = matrix;std::cout << "Using elemPtr: " << std::endl; for (int i = 0 ; i < 2 ; i++) { for (int j = 0 ; j < 3 ; j++) { std::cout << *(elemPtr + i * 3 + j) << " " ; } std::cout << std::endl; } std::cout << "Using rowPtr: " << std::endl; for (int i = 0 ; i < 2 ; i++) { for (int j = 0 ; j < 3 ; j++) { std::cout << rowPtr[i][j] << " " ; } std::cout << std::endl; }
3. 函数指针和指针函数 函数指针是指向函数的指针,而指针函数是返回指针的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int add (int a, int b) { return a + b; } using AddFunction = int (*)(int , int );int * createInt (int value) { return new int (value); } int main () { AddFunction funcPtr = add; int result = funcPtr (5 , 3 ); std::cout << "5 + 3 = " << result << std::endl; int * ptr = createInt (42 ); std::cout << "*ptr = " << *ptr << std::endl; delete ptr; return 0 ; }
4. 成员指针 成员指针用于指向类的成员变量或成员函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Person {public : std::string name; int age; Person (std::string n, int a) : name (n), age (a) {} void greet () { std::cout << "Hello, my name is " << name << std::endl; } }; int main () { Person p ("Alice" , 30 ) ; std::string Person::*namePtr = &Person::name; int Person::*agePtr = &Person::age; std::cout << "Name: " << p.*namePtr << std::endl; std::cout << "Age: " << p.*agePtr << std::endl; void (Person::*greetPtr)() = &Person::greet; (p.*greetPtr)(); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> int main () { int numbers[] = {10 , 20 , 30 , 40 , 50 }; int * ptr = numbers; std::cout << "*ptr = " << *ptr << std::endl; ptr++; std::cout << "*ptr = " << *ptr << std::endl; ptr += 2 ; std::cout << "*ptr = " << *ptr << std::endl; ptr--; std::cout << "*ptr = " << *ptr << std::endl; int * start = numbers; int * end = numbers + 5 ; std::cout << "Array elements: " ; while (start < end) { std::cout << *start << " " ; start++; } std::cout << std::endl; return 0 ; }
指针算术的规则 指针与整数的加减 :
ptr + n:指针向后移动n个元素,实际移动的字节数为 n * sizeof(*ptr)ptr - n:指针向前移动n个元素,实际移动的字节数为 n * sizeof(*ptr)ptr++/++ptr:指针向后移动1个元素ptr--/--ptr:指针向前移动1个元素指针之间的减法 :
ptr2 - ptr1:计算两个指针之间的元素个数,结果为整数类型(ptrdiff_t)要求两个指针指向同一个数组或同一个对象的不同部分 指针比较运算 :
==、!=:判断两个指针是否指向同一位置<、<=、>、>=:判断指针在内存中的相对位置要求两个指针指向同一个数组或同一个对象的不同部分 指针算术的应用场景 数组遍历 :使用指针算术遍历数组元素动态内存管理 :在动态分配的内存块中导航字符串处理 :操作C风格字符串数据结构实现 :实现链表、树等数据结构性能优化 :指针算术通常比数组下标访问更快指针算术的注意事项 越界访问 :指针算术可能导致越界访问,应确保指针始终在有效范围内类型安全 :指针算术依赖于指针的类型,不同类型的指针算术结果不同空指针 :对空指针进行算术运算会导致未定义行为野指针 :对野指针进行算术运算会导致未定义行为void指针 :void指针不支持算术运算,因为它的大小未知指针算术示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> int main () { double values[] = {1.1 , 2.2 , 3.3 , 4.4 , 5.5 }; double * p = values; std::cout << "p = " << p << ", *p = " << *p << std::endl; p += 2 ; std::cout << "p += 2: " << p << ", *p = " << *p << std::endl; double * start = values; double * end = values + 5 ; ptrdiff_t count = end - start; std::cout << "Elements count: " << count << std::endl; if (start < end) { std::cout << "start is before end" << std::endl; } return 0 ; }
指针与数组的关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> int main () { int numbers[] = {10 , 20 , 30 , 40 , 50 }; std::cout << "*numbers = " << *numbers << std::endl; std::cout << "*(numbers + 1) = " << *(numbers + 1 ) << std::endl; int * ptr = numbers; std::cout << "ptr[0] = " << ptr[0 ] << std::endl; std::cout << "ptr[2] = " << ptr[2 ] << std::endl; std::cout << "*(numbers + 3) = " << *(numbers + 3 ) << std::endl; return 0 ; }
指针与函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <iostream> void increment (int * value) { (*value)++; } int * createArray (int size) { int * arr = new int [size]; for (int i = 0 ; i < size; i++) { arr[i] = i * 10 ; } return arr; } int main () { int x = 5 ; std::cout << "Before: x = " << x << std::endl; increment (&x); std::cout << "After: x = " << x << std::endl; int * numbers = createArray (5 ); std::cout << "Array elements: " ; for (int i = 0 ; i < 5 ; i++) { std::cout << numbers[i] << " " ; } std::cout << std::endl; delete [] numbers; return 0 ; }
指向指针的指针 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> int main () { int x = 100 ; int * ptr = &x; int ** ptrToPtr = &ptr; std::cout << "x = " << x << std::endl; std::cout << "*ptr = " << *ptr << std::endl; std::cout << "**ptrToPtr = " << **ptrToPtr << std::endl; **ptrToPtr = 200 ; std::cout << "After modification: " << std::endl; std::cout << "x = " << x << std::endl; std::cout << "*ptr = " << *ptr << std::endl; std::cout << "**ptrToPtr = " << **ptrToPtr << std::endl; return 0 ; }
动态内存分配 new 和 delete 运算符 动态内存分配是C++中管理内存的重要机制,允许程序在运行时根据需要分配和释放内存。new和delete运算符是C++中进行动态内存管理的基本工具,它们提供了类型安全的内存分配和对象生命周期管理。
动态内存分配的底层实现 new运算符的工作原理:
内存分配 :调用operator new函数分配原始内存,该函数内部通常调用malloc内存检查 :检查分配是否成功,失败则抛出std::bad_alloc异常对象构造 :使用 placement new 在分配的内存上调用对象的构造函数返回指针 :返回指向构造好的对象的类型化指针delete运算符的工作原理:
空指针检查 :如果指针为nullptr,直接返回对象析构 :调用对象的析构函数,清理对象资源内存释放 :调用operator delete函数释放内存,该函数内部通常调用free1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Person {public : std::string name; int age; Person (std::string n, int a) : name (n), age (a) { std::cout << "Person constructor called" << std::endl; } ~Person () { std::cout << "Person destructor called" << std::endl; } }; int main () { Person* p = new Person ("Alice" , 30 ); std::cout << "Name: " << p->name << ", Age: " << p->age << std::endl; delete p; return 0 ; }
内存分配器的底层实现 C++允许通过重载operator new和operator delete来自定义内存分配行为,这对于性能优化和特殊内存需求非常重要:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class MemoryPool {private : struct Block { Block* next; char data[1 ]; }; Block* freeList; size_t blockSize; std::mutex mutex; public : MemoryPool (size_t size) : blockSize (size), freeList (nullptr ) {} void * allocate (size_t size) { std::lock_guard<std::mutex> lock (mutex) ; if (freeList) { void * result = freeList->data; freeList = freeList->next; return result; } size_t allocSize = sizeof (Block) + blockSize - 1 ; Block* newBlock = static_cast <Block*>(std::malloc (allocSize)); if (!newBlock) { throw std::bad_alloc (); } return newBlock->data; } void deallocate (void * ptr) { std::lock_guard<std::mutex> lock (mutex) ; Block* block = reinterpret_cast <Block*>( static_cast <char *>(ptr) - offsetof (Block, data) ); block->next = freeList; freeList = block; } }; class PoolAllocated {private : static MemoryPool pool; public : int value; PoolAllocated (int v) : value (v) {} void * operator new (size_t size) { return pool.allocate (size); } void operator delete (void * ptr) noexcept { pool.deallocate (ptr); } }; MemoryPool PoolAllocated::pool (sizeof (PoolAllocated)) ;
内存分配的性能优化 内存池技术 :预分配内存块,减少系统调用开销对齐优化 :确保内存分配满足对齐要求,提高访问效率批量分配 :一次性分配多个对象的内存,减少分配次数缓存友好 :考虑内存局部性,减少缓存未命中定制分配器 :为特定类型设计专用分配器1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 template <typename T, size_t BlockSize = 4096 >class FastMemoryPool {private : union Slot { T object; Slot* next; }; Slot* freeSlots; std::vector<void *> blocks; void allocateBlock () { char * block = new char [BlockSize]; blocks.push_back (block); size_t numSlots = (BlockSize - sizeof (Slot*)) / sizeof (Slot); for (size_t i = 0 ; i < numSlots; i++) { Slot* slot = reinterpret_cast <Slot*>(block + i * sizeof (Slot)); slot->next = freeSlots; freeSlots = slot; } } public : FastMemoryPool () : freeSlots (nullptr ) {} ~FastMemoryPool () { for (void * block : blocks) { delete [] static_cast <char *>(block); } } T* allocate () { if (!freeSlots) { allocateBlock (); } T* result = &freeSlots->object; freeSlots = freeSlots->next; return result; } void deallocate (T* ptr) { Slot* slot = reinterpret_cast <Slot*>(ptr); slot->next = freeSlots; freeSlots = slot; } }; template <typename T>class PoolAllocator {private : FastMemoryPool<T> pool; public : using value_type = T; PoolAllocator () = default ; template <typename U> PoolAllocator (const PoolAllocator<U>&) {} T* allocate (size_t n) { if (n != 1 ) { throw std::bad_alloc (); } return pool.allocate (); } void deallocate (T* ptr, size_t n) { if (n != 1 ) { return ; } pool.deallocate (ptr); } }; std::vector<int , PoolAllocator<int >> vec;
内存分配的安全考虑 内存泄漏 :确保每个new都有对应的delete双重释放 :避免对同一指针多次调用delete悬垂指针 :避免使用已释放的内存内存越界 :确保在分配的内存范围内操作异常安全 :确保内存分配失败时不会导致资源泄漏1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Resource {private : int * data; public : Resource (size_t size) : data (nullptr ) { try { data = new int [size]; } catch (...) { delete [] data; throw ; } } ~Resource () { delete [] data; } Resource (const Resource&) = delete ; Resource& operator =(const Resource&) = delete ; };
自定义内存分配器 C++允许自定义内存分配器,通过重载operator new和operator delete实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 class CustomAllocated {public : int value; CustomAllocated (int v) : value (v) { std::cout << "CustomAllocated constructor" << std::endl; } ~CustomAllocated () { std::cout << "CustomAllocated destructor" << std::endl; } void * operator new (size_t size) { std::cout << "Custom operator new called, size: " << size << std::endl; void * ptr = std::malloc (size); if (!ptr) { throw std::bad_alloc (); } return ptr; } void operator delete (void * ptr) noexcept { std::cout << "Custom operator delete called" << std::endl; std::free (ptr); } void * operator new [](size_t size) { std::cout << "Custom operator new[] called, size: " << size << std::endl; void * ptr = std::malloc (size); if (!ptr) { throw std::bad_alloc (); } return ptr; } void operator delete [](void * ptr) noexcept { std::cout << "Custom operator delete[] called" << std::endl; std::free (ptr); } }; int main () { CustomAllocated* p1 = new CustomAllocated (42 ); std::cout << "p1->value: " << p1->value << std::endl; delete p1; CustomAllocated* pArray = new CustomAllocated[3 ]{1 , 2 , 3 }; for (int i = 0 ; i < 3 ; i++) { std::cout << "pArray[" << i << "].value: " << pArray[i].value << std::endl; } delete [] pArray; return 0 ; }
内存对齐和分配优化 内存对齐对于性能至关重要,C++17引入了std::aligned_alloc函数来分配对齐的内存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <cstdlib> #include <new> int main () { void * ptr = std::aligned_alloc (16 , 1024 ); if (ptr) { std::cout << "Aligned pointer: " << ptr << std::endl; std::cout << "Alignment check: " << (reinterpret_cast <uintptr_t >(ptr) % 16 == 0 ) << std::endl; int * intPtr = new (ptr) int [256 ]; intPtr[0 ] = 42 ; std::cout << "intPtr[0]: " << intPtr[0 ] << std::endl; for (int i = 0 ; i < 256 ; i++) { intPtr[i].~int (); } std::free (ptr); } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <iostream> int main () { int * pInt = new int ; *pInt = 42 ; std::cout << "*pInt = " << *pInt << std::endl; delete pInt; int size = 5 ; int * pArray = new int [size]; for (int i = 0 ; i < size; i++) { pArray[i] = i * 10 ; } std::cout << "Array elements: " ; for (int i = 0 ; i < size; i++) { std::cout << pArray[i] << " " ; } std::cout << std::endl; delete [] pArray; class Person { public : std::string name; int age; Person (std::string n, int a) : name (n), age (a) { std::cout << "Person constructor called" << std::endl; } ~Person () { std::cout << "Person destructor called" << std::endl; } void display () { std::cout << "Name: " << name << ", Age: " << age << std::endl; } }; Person* pPerson = new Person ("Alice" , 30 ); pPerson->display (); delete pPerson; return 0 ; }
智能指针 智能指针是C++11引入的一种RAII(资源获取即初始化)机制,用于自动管理动态内存,避免内存泄漏。智能指针本质上是一个包装了裸指针的类,通过析构函数自动释放所管理的内存,同时提供了类型安全的指针操作接口。
智能指针的底层实现原理 智能指针的核心实现依赖于以下技术:
RAII机制 :在构造时获取资源,在析构时释放资源模板技术 :使用模板实现类型安全的指针包装引用计数 :shared_ptr使用引用计数追踪资源的所有者数量移动语义 :unique_ptr利用移动语义实现独占所有权的转移类型擦除 :支持自定义删除器而不影响智能指针的大小原子操作 :shared_ptr的引用计数操作是线程安全的1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 template <typename T, typename Deleter = std::default_delete<T>> class SimpleUniquePtr { private : T* ptr; Deleter deleter; public : explicit SimpleUniquePtr (T* p = nullptr , const Deleter& d = Deleter()) : ptr(p), deleter(d) { } ~SimpleUniquePtr () { if (ptr) { deleter (ptr); } } SimpleUniquePtr (const SimpleUniquePtr&) = delete ; SimpleUniquePtr& operator =(const SimpleUniquePtr&) = delete ; SimpleUniquePtr (SimpleUniquePtr&& other) noexcept : ptr (other.ptr), deleter (std::move (other.deleter)) { other.ptr = nullptr ; } SimpleUniquePtr& operator =(SimpleUniquePtr&& other) noexcept { if (this != &other) { if (ptr) { deleter (ptr); } ptr = other.ptr; deleter = std::move (other.deleter); other.ptr = nullptr ; } return *this ; } T* get () const { return ptr; } T& operator *() const { return *ptr; } T* operator ->() const { return ptr; } explicit operator bool () const { return ptr != nullptr ; } T* release () { T* temp = ptr; ptr = nullptr ; return temp; } void reset (T* p = nullptr ) { if (ptr) { deleter (ptr); } ptr = p; } }; template <typename T> class SimpleSharedPtr { private : struct ControlBlock { T* ptr; std::atomic<int > refCount; std::atomic<int > weakCount; ControlBlock (T* p) : ptr (p), refCount (1 ), weakCount (0 ) {} ~ControlBlock () { delete ptr; } }; ControlBlock* controlBlock; void incrementRefCount () { if (controlBlock) { controlBlock->refCount.fetch_add (1 , std::memory_order_relaxed); } } void decrementRefCount () { if (controlBlock) { if (controlBlock->refCount.fetch_sub (1 , std::memory_order_acq_rel) == 1 ) { delete controlBlock; controlBlock = nullptr ; } } } public : explicit SimpleSharedPtr (T* p = nullptr ) : controlBlock(p ? new ControlBlock(p) : nullptr) { } SimpleSharedPtr (const SimpleSharedPtr& other) : controlBlock (other.controlBlock) { incrementRefCount (); } SimpleSharedPtr& operator =(const SimpleSharedPtr& other) { if (this != &other) { decrementRefCount (); controlBlock = other.controlBlock; incrementRefCount (); } return *this ; } ~SimpleSharedPtr () { decrementRefCount (); } T* get () const { return controlBlock ? controlBlock->ptr : nullptr ; } T& operator *() const { return *get (); } T* operator ->() const { return get (); } explicit operator bool () const { return get () != nullptr ; } int use_count () const { return controlBlock ? controlBlock->refCount.load (std::memory_order_acquire) : 0 ; } };
智能指针的性能优化 内存布局优化 :
unique_ptr:大小与裸指针相同(无额外开销) shared_ptr:通常是两倍裸指针大小(存储指针和控制块指针) make_shared:一次分配对象和控制块内存,减少内存分配次数 引用计数优化 :
使用原子操作确保线程安全 避免不必要的引用计数操作(使用移动语义) 合理使用weak_ptr打破循环引用 删除器优化 :
空基类优化(EBO)减少删除器的大小开销 函数对象作为删除器比函数指针更高效 小型删除器可以内联存储 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 auto p1 = std::make_shared <Person>("Alice" , 30 ); std::unique_ptr<Person> p2 = std::make_unique <Person>("Bob" , 25 ); std::unique_ptr<Person> p3 = std::move (p2); struct EmptyBase { };struct OptimizedDeleter : private EmptyBase { void operator () (Person* p) const { delete p; } }; std::unique_ptr<Person, OptimizedDeleter> p4 (new Person("Charlie" , 35 )) ;struct CustomAllocator { void * allocate (size_t size) { return std::malloc (size); } void deallocate (void * ptr) { std::free (ptr); } }; CustomAllocator alloc; Person* rawPtr = new Person ("David" , 40 ); std::shared_ptr<Person> p5 (rawPtr, [](Person* p) { delete p; }) ;
智能指针的高级使用场景 自定义删除器的高级应用 :
管理非内存资源(文件句柄、网络连接等) 实现自定义资源释放逻辑 与第三方库集成 智能指针与工厂模式 :
返回智能指针的工厂函数 类型擦除的对象工厂 依赖注入容器 智能指针与线程安全 :
线程间安全传递所有权 共享状态的线程安全管理 避免数据竞争 智能指针与多态 :
基类指针管理派生类对象 运行时多态的安全实现 接口与实现分离 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 class FileGuard {public : explicit FileGuard (FILE* f) : file(f) { } ~FileGuard () { if (file) { fclose (file); } } FILE* get () const { return file; } private : FILE* file; }; std::unique_ptr<FILE, decltype (&fclose) > filePtr (fopen("data.txt" , "r" ), &fclose) ;class Shape {public : virtual void draw () = 0 ; virtual ~Shape () {} }; class Circle : public Shape {public : void draw () override { } }; class Square : public Shape {public : void draw () override { } }; std::unique_ptr<Shape> createShape (const std::string& type) { if (type == "circle" ) { return std::make_unique <Circle>(); } else if (type == "square" ) { return std::make_unique <Square>(); } return nullptr ; } void processData (std::shared_ptr<Data> data) { } std::thread t1 (processData, std::make_shared<Data>()) ;std::thread t2 (processData, std::make_shared<Data>()) ;std::vector<std::unique_ptr<Shape>> shapes; shapes.push_back (std::make_unique <Circle>()); shapes.push_back (std::make_unique <Square>()); for (const auto & shape : shapes) { shape->draw (); }
智能指针的最佳实践 选择合适的智能指针 :
默认使用unique_ptr,当需要共享所有权时使用shared_ptr 避免不必要的shared_ptr,因为引用计数有开销 使用weak_ptr打破循环引用 优先使用工厂函数 :
使用make_unique和make_shared创建智能指针 避免直接使用new初始化智能指针 工厂函数提供更一致的错误处理 传递智能指针的正确方式 :
unique_ptr:使用移动语义传递 shared_ptr:根据需要传递值或const引用 避免裸指针与智能指针混用 智能指针的性能考量 :
注意shared_ptr的引用计数开销 合理使用移动语义减少开销 考虑自定义分配器优化内存使用 智能指针与异常安全 :
智能指针确保即使发生异常也能释放资源 避免在构造函数中使用裸指针分配内存 使用RAII原则管理所有资源 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 auto p1 = std::make_unique <Person>("Alice" , 30 );auto p2 = std::make_shared <Person>("Bob" , 25 );void processUniquePtr (std::unique_ptr<Person> p) { } void processSharedPtr (const std::shared_ptr<Person>& p) { } Person* createPerson () { return new Person ("Charlie" , 35 ); } std::unique_ptr<Person> createPersonSafe () { return std::make_unique <Person>("Charlie" , 35 ); } class ResourceManager {private : std::unique_ptr<Resource> resource1; std::unique_ptr<Resource> resource2; public : ResourceManager () : resource1 (std::make_unique <Resource>()), resource2 (std::make_unique <Resource>()) { } };
智能指针的种类 std::unique_ptr :独占所有权的智能指针std::shared_ptr :共享所有权的智能指针std::weak_ptr :不增加引用计数的shared_ptr观察者1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> #include <memory> class Person {public : std::string name; int age; Person (std::string n, int a) : name (n), age (a) { std::cout << "Person constructor called for " << name << std::endl; } ~Person () { std::cout << "Person destructor called for " << name << std::endl; } void display () { std::cout << "Name: " << name << ", Age: " << age << std::endl; } }; int main () { std::unique_ptr<Person> p1 (new Person("Alice" , 30 )) ; p1->display (); auto p2 = std::make_unique <Person>("Bob" , 25 ); p2->display (); std::unique_ptr<Person> p3 = std::move (p1); if (p1) { p1->display (); } else { std::cout << "p1 is empty" << std::endl; } p3->display (); auto pArray = std::make_unique <Person[]>(3 ); return 0 ; }
std::shared_ptr std::shared_ptr是一种共享所有权的智能指针,多个shared_ptr可以指向同一个对象,通过引用计数管理对象的生命周期:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> #include <memory> int main () { std::shared_ptr<Person> p1 (new Person("Charlie" , 35 )) ; std::cout << "Reference count: " << p1. use_count () << std::endl; auto p2 = std::make_shared <Person>("David" , 40 ); std::cout << "Reference count: " << p2. use_count () << std::endl; std::shared_ptr<Person> p3 = p2; std::cout << "Reference count after copy: " << p2. use_count () << std::endl; std::shared_ptr<Person> p4 = std::move (p1); std::cout << "p1 use_count: " << p1. use_count () << std::endl; std::cout << "p4 use_count: " << p4. use_count () << std::endl; return 0 ; }
std::weak_ptr std::weak_ptr是一种不增加引用计数的智能指针,用于观察shared_ptr管理的对象,避免循环引用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> #include <memory> class Node {public : int value; std::shared_ptr<Node> next; std::weak_ptr<Node> prev; Node (int v) : value (v) { std::cout << "Node constructor called for " << value << std::endl; } ~Node () { std::cout << "Node destructor called for " << value << std::endl; } }; int main () { auto n1 = std::make_shared <Node>(1 ); auto n2 = std::make_shared <Node>(2 ); n1->next = n2; n2->prev = n1; std::weak_ptr<Node> wp = n1; if (auto sp = wp.lock ()) { std::cout << "Node value: " << sp->value << std::endl; } else { std::cout << "Node no longer exists" << std::endl; } std::cout << "n1 use_count: " << n1. use_count () << std::endl; std::cout << "n2 use_count: " << n2. use_count () << std::endl; return 0 ; }
智能指针的高级特性 1. 自定义删除器 智能指针允许指定自定义删除器,用于特殊的资源管理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 void customDeleter (Person* p) { std::cout << "Custom deleter called for " << p->name << std::endl; delete p; } struct Deleter { void operator () (Person* p) const { std::cout << "Functor deleter called for " << p->name << std::endl; delete p; } }; int main () { std::unique_ptr<Person, decltype (&customDeleter) > p1 (new Person("Alice" , 30 ), customDeleter) ; std::unique_ptr<Person, Deleter> p2 (new Person("Bob" , 25 )) ; std::unique_ptr<Person, std::function<void (Person*)>> p3 ( new Person ("Charlie" , 35 ), [](Person* p) { std::cout << "Lambda deleter called for " << p->name << std::endl; delete p; } ); std::shared_ptr<Person> p4 (new Person("David" , 40 ), customDeleter) ; return 0 ; }
2. 智能指针与数组 C++11及以后,智能指针支持管理数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int main () { std::unique_ptr<int []> arr1 (new int [5 ]{1 , 2 , 3 , 4 , 5 }) ; for (int i = 0 ; i < 5 ; i++) { std::cout << arr1[i] << " " ; } std::cout << std::endl; auto arr2 = std::make_unique <int []>(5 ); for (int i = 0 ; i < 5 ; i++) { arr2[i] = i * 2 ; } for (int i = 0 ; i < 5 ; i++) { std::cout << arr2[i] << " " ; } std::cout << std::endl; std::shared_ptr<int > arr3 (new int [5 ]{10 , 20 , 30 , 40 , 50 }, [](int * p) { delete [] p; }) ; for (int i = 0 ; i < 5 ; i++) { std::cout << arr3. get ()[i] << " " ; } std::cout << std::endl; return 0 ; }
3. 智能指针的性能优化 智能指针的性能优化策略:
优先使用make_unique和make_shared :减少内存分配次数,提高缓存一致性合理使用移动语义 :避免shared_ptr的引用计数操作避免不必要的shared_ptr复制 :使用const引用传递shared_ptr注意weak_ptr的使用 :避免循环引用,但也要注意lock()操作的开销选择合适的智能指针 :根据所有权模型选择unique_ptr或shared_ptr1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 auto p1 = std::make_shared <Person>("Alice" , 30 ); std::unique_ptr<Person> p2 = std::make_unique <Person>("Bob" , 25 ); std::unique_ptr<Person> p3 = std::move (p2); void processPerson (const std::shared_ptr<Person>& p) { } class Cache {private : std::unordered_map<std::string, std::weak_ptr<Person>> cache; public : void add (const std::string& key, const std::shared_ptr<Person>& value) { cache[key] = value; } std::shared_ptr<Person> get (const std::string& key) { auto it = cache.find (key); if (it != cache.end ()) { return it->second.lock (); } return nullptr ; } };
4. 智能指针的线程安全性 智能指针的线程安全性:
std::shared_ptr :
引用计数操作是线程安全的(原子操作) 但指向的对象本身不是线程安全的 std::unique_ptr :
不涉及共享状态,线程安全 但同一unique_ptr的移动操作不能在多线程中并发进行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <thread> #include <vector> void incrementRefCount (std::shared_ptr<Person>& p) { std::shared_ptr<Person> localCopy = p; std::lock_guard<std::mutex> lock (mutex) ; localCopy->age++; } int main () { auto p = std::make_shared <Person>("Alice" , 30 ); std::vector<std::thread> threads; for (int i = 0 ; i < 10 ; i++) { threads.emplace_back (incrementRefCount, std::ref (p)); } for (auto & t : threads) { t.join (); } std::cout << "Final age: " << p->age << std::endl; std::cout << "Reference count: " << p.use_count () << std::endl; return 0 ; }
智能指针的原理 RAII机制 :智能指针在构造时获取资源,在析构时释放资源所有权管理 :unique_ptr:独占所有权,不允许复制,只允许移动 shared_ptr:共享所有权,通过引用计数追踪所有者数量 weak_ptr:不拥有所有权,只观察shared_ptr管理的对象 析构函数 :智能指针的析构函数自动调用delete或delete[]释放内存智能指针的使用场景 std::unique_ptr :
独占资源的场景 作为函数返回值 作为类成员变量 管理局部动态对象 std::shared_ptr :
多个所有者共享资源的场景 跨作用域共享对象 复杂数据结构(如链表、树)中的节点 长时间存在的对象 std::weak_ptr :
打破shared_ptr的循环引用 观察对象而不延长其生命周期 缓存场景 智能指针的最佳实践 优先使用make_unique和make_shared :
更安全:避免内存泄漏(如果在构造过程中抛出异常) 更高效:make_shared只分配一次内存(对象和控制块) 选择合适的智能指针 :
默认使用unique_ptr,当需要共享所有权时使用shared_ptr 避免不必要的shared_ptr,因为引用计数有开销 避免裸指针与智能指针混用 :
不要用智能指针管理已经由裸指针管理的内存 不要从智能指针获取裸指针后手动释放 注意循环引用 :
正确处理数组 :
unique_ptr支持数组版本:std::unique_ptr<T[]> shared_ptr需要自定义删除器来管理数组 传递智能指针 :
对于unique_ptr,使用移动语义传递 对于shared_ptr,根据需要传递值或引用 智能指针的性能考量 内存开销 :
unique_ptr:几乎无额外开销,与裸指针大小相同 shared_ptr:有额外开销(控制块、引用计数) weak_ptr:与shared_ptr类似,需要访问控制块 时间开销 :
unique_ptr:操作几乎无开销 shared_ptr:引用计数的增减需要原子操作,有一定开销 make_shared:比直接使用new更高效 优化策略 :
优先使用unique_ptr 合理使用make_shared减少内存分配 避免频繁的shared_ptr复制 注意移动语义的使用 std::unique_ptr std::unique_ptr是一种独占所有权的智能指针,同一时间只能有一个unique_ptr指向同一个对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> #include <memory> class Person {public : std::string name; int age; Person (std::string n, int a) : name (n), age (a) { std::cout << "Person constructor called for " << name << std::endl; } ~Person () { std::cout << "Person destructor called for " << name << std::endl; } void display () { std::cout << "Name: " << name << ", Age: " << age << std::endl; } }; int main () { std::unique_ptr<Person> p1 (new Person("Alice" , 30 )) ; p1->display (); auto p2 = std::make_unique <Person>("Bob" , 25 ); p2->display (); std::unique_ptr<Person> p3 = std::move (p1); if (p1) { p1->display (); } else { std::cout << "p1 is empty" << std::endl; } p3->display (); auto pArray = std::make_unique <Person[]>(3 ); return 0 ; }
std::shared_ptr std::shared_ptr是一种共享所有权的智能指针,多个shared_ptr可以指向同一个对象,通过引用计数管理对象的生命周期:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> #include <memory> int main () { std::shared_ptr<Person> p1 (new Person("Charlie" , 35 )) ; std::cout << "Reference count: " << p1. use_count () << std::endl; auto p2 = std::make_shared <Person>("David" , 40 ); std::cout << "Reference count: " << p2. use_count () << std::endl; std::shared_ptr<Person> p3 = p2; std::cout << "Reference count after copy: " << p2. use_count () << std::endl; std::shared_ptr<Person> p4 = std::move (p1); std::cout << "p1 use_count: " << p1. use_count () << std::endl; std::cout << "p4 use_count: " << p4. use_count () << std::endl; return 0 ; }
std::weak_ptr std::weak_ptr是一种不增加引用计数的智能指针,用于观察shared_ptr管理的对象,避免循环引用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> #include <memory> class Node {public : int value; std::shared_ptr<Node> next; std::weak_ptr<Node> prev; Node (int v) : value (v) { std::cout << "Node constructor called for " << value << std::endl; } ~Node () { std::cout << "Node destructor called for " << value << std::endl; } }; int main () { auto n1 = std::make_shared <Node>(1 ); auto n2 = std::make_shared <Node>(2 ); n1->next = n2; n2->prev = n1; std::weak_ptr<Node> wp = n1; if (auto sp = wp.lock ()) { std::cout << "Node value: " << sp->value << std::endl; } else { std::cout << "Node no longer exists" << std::endl; } std::cout << "n1 use_count: " << n1. use_count () << std::endl; std::cout << "n2 use_count: " << n2. use_count () << std::endl; return 0 ; }
智能指针的原理 RAII机制 :智能指针在构造时获取资源,在析构时释放资源所有权管理 :unique_ptr:独占所有权,不允许复制,只允许移动 shared_ptr:共享所有权,通过引用计数追踪所有者数量 weak_ptr:不拥有所有权,只观察shared_ptr管理的对象 析构函数 :智能指针的析构函数自动调用delete或delete[]释放内存智能指针的使用场景 std::unique_ptr :
独占资源的场景 作为函数返回值 作为类成员变量 管理局部动态对象 std::shared_ptr :
多个所有者共享资源的场景 跨作用域共享对象 复杂数据结构(如链表、树)中的节点 长时间存在的对象 std::weak_ptr :
打破shared_ptr的循环引用 观察对象而不延长其生命周期 缓存场景 智能指针的最佳实践 优先使用make_unique和make_shared :
更安全:避免内存泄漏(如果在构造过程中抛出异常) 更高效:make_shared只分配一次内存(对象和控制块) 选择合适的智能指针 :
默认使用unique_ptr,当需要共享所有权时使用shared_ptr 避免不必要的shared_ptr,因为引用计数有开销 避免裸指针与智能指针混用 :
不要用智能指针管理已经由裸指针管理的内存 不要从智能指针获取裸指针后手动释放 注意循环引用 :
正确处理数组 :
unique_ptr支持数组版本:std::unique_ptr<T[]> shared_ptr需要自定义删除器来管理数组 传递智能指针 :
对于unique_ptr,使用移动语义传递 对于shared_ptr,根据需要传递值或引用 智能指针的性能考量 内存开销 :
unique_ptr:几乎无额外开销,与裸指针大小相同 shared_ptr:有额外开销(控制块、引用计数) weak_ptr:与shared_ptr类似,需要访问控制块 时间开销 :
unique_ptr:操作几乎无开销 shared_ptr:引用计数的增减需要原子操作,有一定开销 make_shared:比直接使用new更高效 优化策略 :
优先使用unique_ptr 合理使用make_shared减少内存分配 避免频繁的shared_ptr复制 注意移动语义的使用 数组和指针的安全性 常见错误 空指针解引用 :尝试访问空指针指向的内存野指针 :指针指向已经释放的内存内存泄漏 :动态分配的内存没有释放数组越界 :访问超出数组范围的元素悬挂引用 :引用指向已经销毁的对象未初始化指针 :使用未初始化的指针指针类型错误 :使用错误类型的指针访问内存内存对齐问题 :访问未对齐的内存导致性能下降或崩溃安全实践 初始化指针 :总是将指针初始化为nullptr或有效的内存地址检查空指针 :在使用指针前检查是否为nullptr使用智能指针 :优先使用unique_ptr和shared_ptr管理动态内存避免裸指针 :尽量减少使用裸指针,尤其是在管理动态内存时使用std::array :对于固定大小的数组,使用std::array替代内置数组使用std::vector :对于可变大小的数组,使用std::vector替代内置数组边界检查 :在访问数组元素时确保不越界使用RAII :使用资源获取即初始化的原则管理资源类型安全 :使用正确的指针类型,避免类型转换错误内存对齐 :确保内存访问符合对齐要求内存安全深入 1. 空指针和野指针的检测 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int * ptr = nullptr ;if (ptr) { *ptr = 42 ; } else { std::cout << "Pointer is null" << std::endl; } std::unique_ptr<int > safePtr = std::make_unique <int >(42 );
2. 数组边界检查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <array> std::array<int , 5> arr = {1 , 2 , 3 , 4 , 5 }; for (size_t i = 0 ; i < arr.size (); i++) { std::cout << arr[i] << " " ; } std::cout << std::endl; #include <vector> std::vector<int > vec = {1 , 2 , 3 , 4 , 5 }; for (size_t i = 0 ; i < vec.size (); i++) { std::cout << vec[i] << " " ; } std::cout << std::endl; for (size_t i = 0 ; i < vec.size (); i++) { try { std::cout << vec.at (i) << " " ; } catch (const std::out_of_range& e) { std::cout << "Out of range: " << e.what () << std::endl; } } std::cout << std::endl;
3. 内存泄漏检测 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 void functionWithNoLeak () { std::unique_ptr<int > ptr = std::make_unique <int >(42 ); } class FileHandle {private : FILE* file; public : explicit FileHandle (const char * filename) { file = fopen (filename, "r" ); } ~FileHandle () { if (file) { fclose (file); } } FileHandle (const FileHandle&) = delete ; FileHandle& operator =(const FileHandle&) = delete ; FileHandle (FileHandle&& other) noexcept : file (other.file) { other.file = nullptr ; } FileHandle& operator =(FileHandle&& other) noexcept { if (this != &other) { if (file) { fclose (file); } file = other.file; other.file = nullptr ; } return *this ; } FILE* get () const { return file; } }; void readFile (const char * filename) { FileHandle handle (filename) ; }
4. 内存安全工具 C++提供了多种工具来帮助检测和防止内存安全问题:
地址 sanitizer (ASAN) :检测内存错误,如缓冲区溢出、使用已释放的内存等内存 sanitizer (MSAN) :检测使用未初始化内存的情况未定义行为 sanitizer (UBSAN) :检测未定义行为,如越界访问、类型转换错误等泄漏 sanitizer (LSAN) :检测内存泄漏Valgrind :内存调试和内存泄漏检测工具1 2 3 4 5 6 7 8 9 10 11 g++ -fsanitize=address -g program.cpp -o program g++ -fsanitize=memory -g program.cpp -o program g++ -fsanitize=undefined -g program.cpp -o program valgrind --leak-check=full ./program
安全编码最佳实践 使用现代C++特性 :
智能指针(unique_ptr, shared_ptr) 标准容器(vector, array, map) RAII原则 移动语义 避免常见陷阱 :
不使用裸指针管理动态内存 不手动释放由智能指针管理的内存 不使用已经释放的指针 不访问超出数组范围的元素 不使用未初始化的变量 防御性编程 :
总是检查函数参数的有效性 对所有输入进行验证 使用异常处理错误情况 记录错误信息 代码审查 :
定期审查代码中的内存操作 使用静态分析工具检测潜在问题 编写单元测试验证内存安全性 性能与安全的平衡 :
在性能关键路径上,可以使用裸指针,但要确保安全 使用内存池减少内存分配开销 合理使用内联汇编提高性能,但要注意安全 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 void processLargeArray (const std::vector<int >& data) { const size_t size = data.size (); for (auto it = data.begin (); it != data.end (); ++it) { processElement (*it); } } class MemoryPool {private : std::vector<char > buffer; size_t next; public : explicit MemoryPool (size_t size) : buffer(size), next(0 ) { } void * allocate (size_t bytes) { if (next + bytes > buffer.size ()) { return nullptr ; } void * ptr = &buffer[next]; next += bytes; return ptr; } void reset () { next = 0 ; } }; void processWithPool () { MemoryPool pool (1024 * 1024 ) ; int * arr = static_cast <int *>(pool.allocate (1000 * sizeof (int ))); if (arr) { for (int i = 0 ; i < 1000 ; i++) { arr[i] = i; } pool.reset (); } }
数组和指针的应用 字符串处理 字符串是数组和指针的重要应用场景,C++提供了多种字符串处理方式:
1. C风格字符串的底层实现 C风格字符串是由字符组成的数组,以空字符('\0')结尾:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> #include <cstring> int main () { char str1[] = "Hello" ; char str2[20 ]; std::strcpy (str2, str1); std::cout << "str2: " << str2 << std::endl; std::strcat (str2, " World" ); std::cout << "str2 after concatenation: " << str2 << std::endl; std::cout << "Length of str2: " << std::strlen (str2) << std::endl; char str3[] = "Hello" ; char str4[] = "World" ; int result = std::strcmp (str3, str4); if (result < 0 ) { std::cout << str3 << " is less than " << str4 << std::endl; } else if (result > 0 ) { std::cout << str3 << " is greater than " << str4 << std::endl; } else { std::cout << str3 << " is equal to " << str4 << std::endl; } char str5[] = "Hello, World!" ; char * found = std::strchr (str5, 'W' ); if (found) { std::cout << "Found 'W' at position: " << found - str5 << std::endl; } found = std::strstr (str5, "World" ); if (found) { std::cout << "Found 'World' at position: " << found - str5 << std::endl; } return 0 ; }
2. 字符串处理的性能优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 void processString (const char * str) { size_t len = std::strlen (str); for (size_t i = 0 ; i < len; i++) { } } void concatenateStrings (char * dest, size_t destSize, const char * src1, const char * src2) { size_t src1Len = std::strlen (src1); size_t src2Len = std::strlen (src2); if (src1Len + src2Len < destSize - 1 ) { std::strcpy (dest, src1); std::strcat (dest, src2); } else { } } void fastStringCopy (char * dest, const char * src, size_t len) { std::memcpy (dest, src, len); dest[len] = '\0' ; } #include <string_view> void processStringView (std::string_view sv) { std::cout << "Length: " << sv.length () << std::endl; std::cout << "Substring: " << sv.substr (0 , 5 ) << std::endl; } int main () { const char * cstr = "Hello, World!" ; std::string str = "Hello, C++!" ; processStringView (cstr); processStringView (str); processStringView ("Hello, string_view!" ); return 0 ; }
3. 字符串处理的安全实践 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 void safeStringCopy (char * dest, size_t destSize, const char * src) { if (dest && src && destSize > 0 ) { std::strncpy (dest, src, destSize - 1 ); dest[destSize - 1 ] = '\0' ; } } void safeStringConcat (char * dest, size_t destSize, const char * src) { if (dest && src && destSize > 0 ) { size_t destLen = std::strlen (dest); if (destLen < destSize - 1 ) { size_t srcLen = std::strlen (src); size_t copyLen = std::min (srcLen, destSize - destLen - 1 ); std::strncpy (dest + destLen, src, copyLen); dest[destLen + copyLen] = '\0' ; } } } #include <string> std::string safeConcatenate (const std::string& s1, const std::string& s2) { return s1 + s2; } int main () { char buffer[20 ]; safeStringCopy (buffer, sizeof (buffer), "Hello" ); std::cout << "Buffer: " << buffer << std::endl; safeStringConcat (buffer, sizeof (buffer), " World" ); std::cout << "Buffer after concat: " << buffer << std::endl; std::string s1 = "Hello" ; std::string s2 = " World" ; std::string s3 = safeConcatenate (s1, s2); std::cout << "s3: " << s3 << std::endl; return 0 ; }
函数参数 数组和指针作为函数参数是C++中常见的用法,掌握其高级技巧对于编写高效、安全的代码至关重要。
1. 数组作为函数参数的底层机制 当数组作为函数参数传递时,会发生数组到指针的衰减 ,即数组名会被转换为指向第一个元素的指针:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <iostream> void printArray (int arr[], int size) { std::cout << "Size of arr parameter: " << sizeof (arr) << std::endl; for (int i = 0 ; i < size; i++) { std::cout << arr[i] << " " ; } std::cout << std::endl; } void modifyArray (int * arr, int size) { for (int i = 0 ; i < size; i++) { arr[i] *= 2 ; } } template <size_t N>void printArrayWithReference (int (&arr)[N]) { std::cout << "Size of arr reference: " << sizeof (arr) << std::endl; for (int i = 0 ; i < N; i++) { std::cout << arr[i] << " " ; } std::cout << std::endl; } int main () { int numbers[] = {1 , 2 , 3 , 4 , 5 }; int size = sizeof (numbers) / sizeof (numbers[0 ]); std::cout << "Size of numbers array: " << sizeof (numbers) << std::endl; std::cout << "Original array: " ; printArray (numbers, size); modifyArray (numbers, size); std::cout << "Modified array: " ; printArray (numbers, size); std::cout << "Using reference: " ; printArrayWithReference (numbers); return 0 ; }
2. 高级参数传递技巧 2.1 传递数组的多种方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <iostream> #include <array> #include <vector> void processArrayTraditional (int * arr, size_t size) { for (size_t i = 0 ; i < size; i++) { arr[i] *= 2 ; } } template <typename T, size_t N>void processArrayReference (T (&arr)[N]) { for (size_t i = 0 ; i < N; i++) { arr[i] *= 2 ; } } template <typename T, size_t N>void processStdArray (std::array<T, N>& arr) { for (auto & element : arr) { element *= 2 ; } } template <typename T>void processVector (std::vector<T>& vec) { for (auto & element : vec) { element *= 2 ; } } template <typename T>void processPointerRange (T* begin, T* end) { for (T* ptr = begin; ptr != end; ++ptr) { *ptr *= 2 ; } } template <typename Iterator>void processRange (Iterator begin, Iterator end) { for (auto it = begin; it != end; ++it) { *it *= 2 ; } } int main () { int arr[] = {1 , 2 , 3 , 4 , 5 }; size_t size = sizeof (arr) / sizeof (arr[0 ]); std::array<int , 5> stdArr = {1 , 2 , 3 , 4 , 5 }; std::vector<int > vec = {1 , 2 , 3 , 4 , 5 }; processArrayTraditional (arr, size); processArrayReference (arr); processStdArray (stdArr); processVector (vec); processPointerRange (arr, arr + size); processRange (stdArr.begin (), stdArr.end ()); return 0 ; }
2.2 指针参数的性能优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 void processSmallObject (int value) { std::cout << "Value: " << value << std::endl; } void processLargeObject (const std::string& str) { std::cout << "String length: " << str.length () << std::endl; } void processOptionalObject (const int * ptr) { if (ptr) { std::cout << "Value: " << *ptr << std::endl; } else { std::cout << "No value provided" << std::endl; } } void calculateValues (int input, int & output1, int & output2) { output1 = input * 2 ; output2 = input * 3 ; } void processWithMove (std::string&& str) { std::cout << "String: " << str << std::endl; } int main () { int x = 42 ; std::string str = "Hello, C++!" ; processSmallObject (x); processLargeObject (str); processOptionalObject (&x); processOptionalObject (nullptr ); int a, b; calculateValues (x, a, b); std::cout << "a: " << a << ", b: " << b << std::endl; processWithMove (std::move (str)); return 0 ; }
2.3 类型安全的参数传递 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 enum class Color { Red, Green, Blue }; void setColor (Color color) { switch (color) { case Color::Red: std::cout << "Red" << std::endl; break ; case Color::Green: std::cout << "Green" << std::endl; break ; case Color::Blue: std::cout << "Blue" << std::endl; break ; } } using Meter = double ;using Kilometer = double ;Kilometer metersToKilometers (Meter meters) { return meters / 1000.0 ; } struct PersonInfo { std::string name; int age; std::string address; }; void processPerson (const PersonInfo& info) { std::cout << "Name: " << info.name << std::endl; std::cout << "Age: " << info.age << std::endl; std::cout << "Address: " << info.address << std::endl; } #include <concepts> template <typename T>concept Numeric = std::integral<T> || std::floating_point<T>;template <Numeric T>T sum (T a, T b) { return a + b; } int main () { setColor (Color::Red); Meter distance = 5000.0 ; Kilometer km = metersToKilometers (distance); std::cout << "Distance: " << km << " km" << std::endl; PersonInfo info = {"Alice" , 30 , "123 Main St" }; processPerson (info); std::cout << "Sum: " << sum (1 , 2 ) << std::endl; std::cout << "Sum: " << sum (1.5 , 2.5 ) << std::endl; return 0 ; }
多维数组和指针 多维数组是数组的扩展,它在内存中以连续的方式存储,掌握其内存布局和指针操作对于编写高效的代码至关重要。
1. 多维数组的内存布局 多维数组在内存中是按行优先 的顺序连续存储的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <iostream> int main () { int matrix[2 ][3 ] = {{1 , 2 , 3 }, {4 , 5 , 6 }}; std::cout << "Memory layout of 2D array:" << std::endl; for (int i = 0 ; i < 2 ; i++) { for (int j = 0 ; j < 3 ; j++) { std::cout << "matrix[" << i << "][" << j << "] = " << matrix[i][j]; std::cout << " (address: " << &matrix[i][j] << ")" << std::endl; } } int cube[2 ][2 ][2 ] = {{{1 , 2 }, {3 , 4 }}, {{5 , 6 }, {7 , 8 }}}; std::cout << "\nMemory layout of 3D array:" << std::endl; for (int i = 0 ; i < 2 ; i++) { for (int j = 0 ; j < 2 ; j++) { for (int k = 0 ; k < 2 ; k++) { std::cout << "cube[" << i << "][" << j << "][" << k << "] = " << cube[i][j][k]; std::cout << " (address: " << &cube[i][j][k] << ")" << std::endl; } } } return 0 ; }
2. 多维数组的指针操作 2.1 指向多维数组的指针类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> int main () { int matrix[2 ][3 ] = {{1 , 2 , 3 }, {4 , 5 , 6 }}; int * elemPtr = &matrix[0 ][0 ]; std::cout << "Using element pointer:" << std::endl; for (int i = 0 ; i < 2 * 3 ; i++) { std::cout << elemPtr[i] << " " ; } std::cout << std::endl; int (*rowPtr)[3 ] = matrix; std::cout << "Using row pointer:" << std::endl; for (int i = 0 ; i < 2 ; i++) { for (int j = 0 ; j < 3 ; j++) { std::cout << rowPtr[i][j] << " " ; } std::cout << std::endl; } int (*arrPtr)[2 ][3 ] = &matrix; std::cout << "Using array pointer:" << std::endl; for (int i = 0 ; i < 2 ; i++) { for (int j = 0 ; j < 3 ; j++) { std::cout << (*arrPtr)[i][j] << " " ; } std::cout << std::endl; } return 0 ; }
2.2 多维数组作为函数参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 #include <iostream> void print2DArrayFixedCols (int arr[][3 ], int rows) { for (int i = 0 ; i < rows; i++) { for (int j = 0 ; j < 3 ; j++) { std::cout << arr[i][j] << " " ; } std::cout << std::endl; } } void print2DArrayPointer (int (*arr)[3 ], int rows) { for (int i = 0 ; i < rows; i++) { for (int j = 0 ; j < 3 ; j++) { std::cout << arr[i][j] << " " ; } std::cout << std::endl; } } template <size_t Rows, size_t Cols> void print2DArrayTemplate (int (&arr)[Rows][Cols]) { for (size_t i = 0 ; i < Rows; i++) { for (size_t j = 0 ; j < Cols; j++) { std::cout << arr[i][j] << " " ; } std::cout << std::endl; } } void printUsingDoublePointer (int ** arr, int rows, int cols) { for (int i = 0 ; i < rows; i++) { for (int j = 0 ; j < cols; j++) { std::cout << arr[i][j] << " " ; } std::cout << std::endl; } } int main () { int matrix[2 ][3 ] = {{1 , 2 , 3 }, {4 , 5 , 6 }}; std::cout << "Static 2D array (fixed cols): " << std::endl; print2DArrayFixedCols (matrix, 2 ); std::cout << "Static 2D array (pointer): " << std::endl; print2DArrayPointer (matrix, 2 ); std::cout << "Static 2D array (template): " << std::endl; print2DArrayTemplate (matrix); int rows = 2 ; int cols = 3 ; int ** dynamicMatrix = new int *[rows]; for (int i = 0 ; i < rows; i++) { dynamicMatrix[i] = new int [cols]; } int value = 1 ; for (int i = 0 ; i < rows; i++) { for (int j = 0 ; j < cols; j++) { dynamicMatrix[i][j] = value++; } } std::cout << "Dynamic 2D array: " << std::endl; printUsingDoublePointer (dynamicMatrix, rows, cols); for (int i = 0 ; i < rows; i++) { delete [] dynamicMatrix[i]; } delete [] dynamicMatrix; return 0 ; }
2.3 多维数组的性能优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 #include <iostream> #include <chrono> void traditionalAccess (int ** matrix, int rows, int cols) { for (int i = 0 ; i < rows; i++) { for (int j = 0 ; j < cols; j++) { matrix[i][j] = i * cols + j; } } } void linearAccess (int * matrix, int rows, int cols) { for (int i = 0 ; i < rows; i++) { for (int j = 0 ; j < cols; j++) { matrix[i * cols + j] = i * cols + j; } } } #include <memory> std::unique_ptr<int []> create2DArray (int rows, int cols) { auto matrix = std::make_unique <int []>(rows * cols); for (int i = 0 ; i < rows; i++) { for (int j = 0 ; j < cols; j++) { matrix[i * cols + j] = i * cols + j; } } return matrix; } int main () { const int rows = 1000 ; const int cols = 1000 ; int ** matrix1 = new int *[rows]; for (int i = 0 ; i < rows; i++) { matrix1[i] = new int [cols]; } auto start1 = std::chrono::high_resolution_clock::now (); traditionalAccess (matrix1, rows, cols); auto end1 = std::chrono::high_resolution_clock::now (); std::chrono::duration<double > elapsed1 = end1 - start1; std::cout << "Traditional access time: " << elapsed1. count () << " seconds" << std::endl; for (int i = 0 ; i < rows; i++) { delete [] matrix1[i]; } delete [] matrix1; int * matrix2 = new int [rows * cols]; auto start2 = std::chrono::high_resolution_clock::now (); linearAccess (matrix2, rows, cols); auto end2 = std::chrono::high_resolution_clock::now (); std::chrono::duration<double > elapsed2 = end2 - start2; std::cout << "Linear access time: " << elapsed2. count () << " seconds" << std::endl; delete [] matrix2; auto start3 = std::chrono::high_resolution_clock::now (); auto matrix3 = create2DArray (rows, cols); auto end3 = std::chrono::high_resolution_clock::now (); std::chrono::duration<double > elapsed3 = end3 - start3; std::cout << "Smart pointer time: " << elapsed3. count () << " seconds" << std::endl; return 0 ; }
2.4 多维数组的现代C++替代方案 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> #include <vector> #include <array> int main () { std::vector<std::vector<int >> vec2D = { {1 , 2 , 3 }, {4 , 5 , 6 }, {7 , 8 , 9 } }; std::cout << "Using std::vector:" << std::endl; for (const auto & row : vec2D) { for (int value : row) { std::cout << value << " " ; } std::cout << std::endl; } std::array<std::array<int , 3>, 2> arr2D = { {{1 , 2 , 3 }, {4 , 5 , 6 }} }; std::cout << "Using std::array:" << std::endl; for (const auto & row : arr2D) { for (int value : row) { std::cout << value << " " ; } std::cout << std::endl; } int rows = 2 ; int cols = 3 ; std::vector<int > flatVec (rows * cols) ; for (int i = 0 ; i < rows; i++) { for (int j = 0 ; j < cols; j++) { flatVec[i * cols + j] = i * cols + j + 1 ; } } std::cout << "Using flat std::vector:" << std::endl; for (int i = 0 ; i < rows; i++) { for (int j = 0 ; j < cols; j++) { std::cout << flatVec[i * cols + j] << " " ; } std::cout << std::endl; } return 0 ; }
总结 数组和指针是C++中最核心、最强大的概念之一,它们构成了C++高效性能的基础。通过本章的深入学习,你应该掌握了以下关键知识点:
核心概念与原理 数组的底层实现 :
连续内存存储与固定大小 数组到指针的衰减机制 多维数组的行优先存储布局 数组的类型特性与引用 指针的高级特性 :
指针算术的底层原理与边界检查 指针类型与类型安全 函数指针与成员指针的应用 指针与引用的区别和联系 动态内存管理 :
new/delete的底层实现与重载内存对齐与性能优化 自定义内存分配器的设计 内存池技术与缓存友好性 智能指针的设计与实现 :
std::unique_ptr的独占所有权语义std::shared_ptr的引用计数机制std::weak_ptr的循环引用解决方案自定义删除器的应用场景 现代C++实践 现代C++特性 :
移动语义与智能指针的结合 std::make_unique与std::make_shared的最佳实践std::array与std::vector的性能对比std::string_view与现代字符串处理性能优化策略 :
利用内存连续性提高访问速度 减少指针解引用开销 合理选择参数传递方式 内存池与缓存友好的数据结构设计 内存安全实践 :
智能指针替代裸指针的最佳实践 RAII原则管理资源 内存安全工具(ASAN、MSAN、UBSAN)的使用 防御性编程技巧与错误处理 工程应用与最佳实践 字符串处理 :
C风格字符串的底层操作与优化 std::string的SSO与高级特性Unicode处理与国际化支持 字符串处理的性能优化策略 函数参数传递 :
数组与指针作为参数的最佳实践 类型安全的参数设计 性能与安全性的平衡 现代C++的参数传递方式 多维数据处理 :
多维数组的内存布局与访问模式 连续内存与分散内存的性能对比 现代C++的多维数据结构 大型数据的分块处理与并行计算 跨平台与兼容性 :
指针大小与内存模型的平台差异 内存对齐的跨平台处理 不同编译器的实现差异 可移植代码的编写技巧 专业发展方向 系统编程 :
内存管理与优化 指针操作与位运算 硬件接口与设备驱动开发 性能优化 :
安全编程 :
内存安全漏洞防护 指针操作的安全性 静态分析与代码审查 现代C++设计 :
智能指针的高级应用 内存管理的抽象设计 资源管理的最佳实践 学习建议 深入理解内存模型 :
了解计算机内存的工作原理 掌握C++的内存管理机制 学习现代操作系统的内存管理 实践与实验 :
编写各种数组和指针的示例代码 测试不同内存访问模式的性能 分析智能指针的行为与性能 阅读优秀代码 :
学习标准库的实现 研究开源项目中的内存管理 分析高性能C++代码的设计 持续学习 :
关注C++标准的发展 学习现代C++的最佳实践 了解行业前沿的内存管理技术 数组和指针不仅是C++的基础,也是理解计算机系统和编写高性能代码的关键。通过深入掌握这些概念,你将能够编写更加高效、安全、可维护的C++代码,为成为一名专业的C++开发者打下坚实的基础。