
一、c中string与string.h的比较基本区别特性string(C风格)string.h(C风格)本质C 类C 库头文件头文件stringstring.h或cstring类型std::string类char*字符数组内存管理自动管理手动管理操作方式成员函数/操作符全局函数代码对比C风格 string.h#include cstring // 或 string.h int main() { // 需要手动管理内存 char str1[100] Hello; char str2[] World; char result[100]; // 使用全局函数操作 strcpy(result, str1); // 复制 strcat(result, str2); // 连接 int len strlen(result); // 长度 int cmp strcmp(str1, Hello); // 比较 cout result endl; // Hello World cout 长度: len endl; return 0; }C风格 string#include string #include iostream using namespace std; int main() { // 自动管理内存 string str1 Hello; string str2 World; // 使用操作符更直观 string result str1 str2; // 连接 int len result.length(); // 长度 bool cmp (str1 Hello); // 比较 // 更多功能 result !; // 追加 char ch result[0]; // 索引访问 string sub result.substr(0, 5); // 子串 cout result endl; // Hello World! cout 长度: len endl; return 0; }详细对比1. 字符串操作对比操作C风格 (string.h)C风格 (string)复制strcpy(dest, src)str2 str1连接strcat(dest, src)str3 str1 str2长度strlen(str)str.length()或str.size()比较strcmp(s1, s2)s1 s2,s1 s2查找strstr(str, sub)str.find(sub)子串手动复制str.substr(pos, n)C string 类常用功能速查表类别操作示例说明构造默认构造string s;创建空字符串C字符串构造string s(hello);从const char*构造重复字符string s(5, A);创建AAAAA拷贝构造string s2(s1);复制另一个string子串构造string s(s1, 1, 3);从s1的位置1取3个字符迭代器范围string s(beg, end);从迭代器范围构造初始化列表string s{a,b,c};C11赋值直接赋值s hello;赋值运算符assigns.assign(hello);等同于assign(部分)s.assign(hello, 3);取前3个字符helassign(重复)s.assign(5, X);赋值为XXXXX访问索引s[1]不检查边界可修改at()s.at(1)检查边界越界抛异常front()s.front()第一个字符 (C11)back()s.back()最后一个字符 (C11)c_str()s.c_str()返回const char*data()s.data()返回字符数组指针大小length()s.length()字符串长度size()s.size()同length()empty()s.empty()是否为空capacity()s.capacity()当前分配的内存大小reserve()s.reserve(100);预分配空间shrink_to_fit()s.shrink_to_fit();释放多余内存(C11)连接 运算符s3 s1 s2;字符串连接 运算符s1 s2;追加append()s.append(world);追加字符串append(部分)s.append(world, 3);追加前3个字符worappend(重复)s.append(3, !);追加!!!push_back()s.push_back(?);追加单个字符pop_back()s.pop_back();删除最后一个字符插入insert(位置)s.insert(5, xxx);在位置5插入insert(重复)s.insert(0, 3, *);插入3个*insert(迭代器)s.insert(it, X);在迭代器位置插入insert(范围)s.insert(it, beg, end);插入迭代器范围删除erase(位置)s.erase(0, 3);删除前3个字符erase(迭代器)s.erase(it);删除迭代器指向字符erase(范围)s.erase(beg, end);删除迭代器范围clear()s.clear();清空字符串替换replace(位置)s.replace(2, 4, xx);替换子串replace(迭代器)s.replace(beg, end, xx);替换迭代器范围replace(重复)s.replace(0, 2, 3, A);替换为3个A查找find()s.find(world);正向查找rfind()s.rfind(world);反向查找find_first_of()s.find_first_of(aeiou);查找任意字符首次出现find_last_of()s.find_last_of(aeiou);查找任意字符最后出现find_first_not_of()s.find_first_not_of(aeiou);查找第一个非指定字符find_last_not_of()s.find_last_not_of(aeiou);查找最后一个非指定字符nposif(pos string::npos)查找失败标志子串substr()s.substr(6);从位置6到结束substr(范围)s.substr(0, 5);从0取5个字符比较compare()s.compare(hello);返回负数/0/正数运算符if(s1 s2), !, , , , 转换to_string()string s to_string(123);数字转字符串(C11)stoi()int i stoi(456);字符串转int(C11)stol()long l stol(789);转longstof()float f stof(3.14);转floatstod()double d stod(2.718);转double大小写transform(s.begin(),s.end(),s.begin(),::toupper);转大写大小写transform(s.begin(),s.end(),s.begin(),::tolower);转小写迭代器begin()/end()auto it s.begin();正向迭代器cbegin()/cend()auto it s.cbegin();常量正向迭代器rbegin()/rend()auto it s.rbegin();反向迭代器crbegin()/crend()auto it s.crbegin();常量反向迭代器范围forfor(char c : s)C11范围循环二、运算符的重载1. 基本概念运算符重载允许我们为自定义类型定义运算符的行为使对象可以像内置类型一样使用运算符。class Complex { private: double real, imag; public: Complex(double r 0, double i 0) : real(r), imag(i) {} // 运算符重载为成员函数 Complex operator(const Complex other) const { return Complex(real other.real, imag other.imag); } void display() const { cout real imag i endl; } }; int main() { Complex c1(3, 4), c2(1, 2); Complex c3 c1 c2; // 使用重载的 运算符 c3.display(); // 4 6i return 0; }2. 运算符重载的两种形式2.1 成员函数形式class Example { private: int value; public: Example(int v 0) : value(v) {} // 成员函数形式左操作数是当前对象 Example operator(const Example other) const { return Example(value other.value); } Example operator(const Example other) { value other.value; return *this; } // 前置 Example operator() { value; return *this; } // 后置 Example operator(int) { Example temp(*this); value; return temp; } };2.2 友元函数形式class Example { private: int value; public: Example(int v 0) : value(v) {} // 友元函数形式可以处理左操作数不是当前对象的情况 friend Example operator(const Example a, const Example b); friend ostream operator(ostream os, const Example e); friend istream operator(istream is, Example e); }; // 全局友元函数定义 Example operator(const Example a, const Example b) { return Example(a.value b.value); } ostream operator(ostream os, const Example e) { os Value: e.value; return os; } istream operator(istream is, Example e) { is e.value; return is; }三、构造函数1. 基本概念构造函数对象创建时自动调用用于初始化对象class Example { public: // 构造函数与类同名无返回值 Example() { cout 构造函数被调用 endl; } // 析构函数~类名无参数无返回值 ~Example() { cout 析构函数被调用 endl; } }; int main() { Example e; // 构造输出构造函数被调用 // ... 使用e return 0; // 析构输出析构函数被调用 }2. 构造函数的种类2.1 默认构造函数class Student { private: string name; int age; public: // 默认构造函数无参数 Student() { name Unknown; age 0; cout 默认构造函数 endl; } // 或者使用初始化列表 // Student() : name(Unknown), age(0) {} void display() const { cout 姓名: name , 年龄: age endl; } }; int main() { Student s1; // 调用默认构造函数 Student s2{}; // C11 统一初始化也调用默认构造函数 Student* s3 new Student(); // 动态创建也调用默认构造函数 s1.display(); delete s3; return 0; }2.2 参数化构造函数class Student { private: string name; int age; double score; public: // 参数化构造函数 Student(string n, int a, double s) { name n; age a; score s; cout 参数化构造函数 endl; } // 更好的方式使用初始化列表 // Student(string n, int a, double s) : name(n), age(a), score(s) {} void display() const { cout name , age 岁, 成绩: score endl; } }; int main() { Student s1(张三, 18, 95.5); // 调用参数化构造函数 Student s2{李四, 19, 88.0}; // C11 统一初始化 s1.display(); s2.display(); return 0; }2.3 拷贝构造函数class Student { private: string name; int* scores; // 动态数组 int count; public: // 普通构造函数 Student(string n, int c) : name(n), count(c) { scores new int[count]; for (int i 0; i count; i) { scores[i] 0; } cout 普通构造函数 endl; } // 拷贝构造函数深拷贝 Student(const Student other) : name(other.name), count(other.count) { scores new int[count]; // 分配新内存 for (int i 0; i count; i) { scores[i] other.scores[i]; // 复制数据 } cout 拷贝构造函数 endl; } // 析构函数 ~Student() { delete[] scores; cout 析构函数: name endl; } void setScore(int index, int value) { if (index 0 index count) { scores[index] value; } } void display() const { cout name : ; for (int i 0; i count; i) { cout scores[i] ; } cout endl; } }; int main() { Student s1(张三, 3); s1.setScore(0, 90); s1.setScore(1, 85); s1.setScore(2, 95); Student s2 s1; // 调用拷贝构造函数深拷贝 s2.setScore(0, 100); // 修改s2不影响s1 s1.display(); // 张三: 90 85 95 s2.display(); // 张三: 100 85 95 return 0; }四、析构函数1. 基本概念析构函数是类的特殊成员函数在对象销毁时自动调用用于释放对象占用的资源。class Example { public: // 构造函数 Example() { cout 对象创建 endl; } // 析构函数~类名无参数无返回值 ~Example() { cout 对象销毁 endl; } }; int main() { Example e; // 构造 // ... 使用e return 0; // 析构自动调用 }2. 析构函数的特点特性说明名称~类名()参数无参数返回值无重载不能重载只有一个析构函数调用自动调用对象生命周期结束默认生成如果没有定义编译器自动生成virtual可以作为虚函数3. 析构函数的典型用途3.1 释放动态内存class DynamicArray { private: int* data; size_t size; public: // 构造函数分配内存 DynamicArray(size_t n) : size(n) { data new int[n]; // 分配堆内存 cout 分配 n 个整数 endl; } // 析构函数释放内存 ~DynamicArray() { delete[] data; // 释放堆内存 cout 释放内存 endl; } void setValue(size_t index, int value) { if (index size) data[index] value; } }; int main() { DynamicArray arr(10); // 分配内存 arr.setValue(0, 100); // ... 使用arr return 0; // 自动调用析构函数释放内存 }3.2 释放其他资源class ResourceHandler { private: FILE* file; mutex* mtx; int* socket; public: ResourceHandler(const char* filename) { // 获取多种资源 file fopen(filename, w); mtx new mutex(); socket new int(1234); if (!file || !mtx || !socket) { // 如果任何资源获取失败需要清理已获取的资源 cleanup(); throw runtime_error(资源获取失败); } cout 资源获取成功 endl; } // 析构函数释放所有资源 ~ResourceHandler() { cleanup(); } private: void cleanup() { if (file) { fclose(file); file nullptr; } if (mtx) { delete mtx; mtx nullptr; } if (socket) { delete socket; socket nullptr; } cout 资源释放完成 endl; } };五、缺省提供在 C 中如果一个类没有显式定义某些成员函数编译器会自动生成缺省提供一些特殊的成员函数。根据不同的 C 版本标准自动生成的函数数量有所不同。现代 CC11 及以后6 个或 8 个C11 引入了移动语义增加了两个新的特殊成员函数。因此现代 C 中的空类会自动生成 6 个函数特殊成员函数默认构造函数析构函数拷贝构造函数拷贝赋值运算符移动构造函数ClassName(ClassName)移动赋值运算符ClassName operator(ClassName)注意这 6 个函数统称为特殊成员函数。编译器生成它们是有条件的例如只有当代码中实际使用了移动操作且没有定义拷贝/析构/移动操作时移动函数才会被隐式定义。六、C 浅拷贝 vs 深拷贝1. 基本概念浅拷贝Shallow Copy简单地复制指针的值内存地址多个对象指向同一块内存空间默认的拷贝构造函数和赋值运算符执行浅拷贝深拷贝Deep Copy复制指针指向的实际数据为新对象分配独立的内存空间需要自定义拷贝构造函数和赋值运算符2. 代码示例对比#include iostream #include cstring using namespace std; // 浅拷贝示例有问题的类 class ShallowString { private: char* str; public: ShallowString(const char* s nullptr) { if (s) { str new char[strlen(s) 1]; strcpy(str, s); } else { str nullptr; } } // 使用默认拷贝构造函数浅拷贝 // ~ShallowString() 析构函数 void print() const { if (str) cout str endl; else cout null endl; } ~ShallowString() { delete[] str; } }; // 深拷贝示例正确的类 class DeepString { private: char* str; public: DeepString(const char* s nullptr) { if (s) { str new char[strlen(s) 1]; strcpy(str, s); } else { str nullptr; } } // 深拷贝构造函数 DeepString(const DeepString other) { if (other.str) { str new char[strlen(other.str) 1]; strcpy(str, other.str); } else { str nullptr; } } // 深拷贝赋值运算符 DeepString operator(const DeepString other) { if (this ! other) { // 自赋值检查 delete[] str; // 释放原有资源 if (other.str) { str new char[strlen(other.str) 1]; strcpy(str, other.str); } else { str nullptr; } } return *this; } void print() const { if (str) cout str endl; else cout null endl; } ~DeepString() { delete[] str; } }; // 演示浅拷贝问题 void demonstrateShallowCopy() { cout 浅拷贝问题演示 endl; ShallowString s1(Hello); ShallowString s2(s1); // 浅拷贝 cout s1: ; s1.print(); // Hello cout s2: ; s2.print(); // Hello // 程序结束时s1和s2的析构函数都会尝试释放同一块内存 // 导致 double delete 错误 } // 演示深拷贝正确性 void demonstrateDeepCopy() { cout 深拷贝正确演示 endl; DeepString s1(Hello); DeepString s2(s1); // 深拷贝 DeepString s3(World); s3 s1; // 深拷贝赋值 cout s1: ; s1.print(); // Hello cout s2: ; s2.print(); // Hello (独立的内存) cout s3: ; s3.print(); // Hello (独立的内存) // 修改s1的内容如果类支持修改 // s2和s3不受影响 cout 所有对象都有自己独立的内存空间 endl; } int main() { // 演示浅拷贝问题 // demonstrateShallowCopy(); // 取消注释会崩溃 // 演示深拷贝正确性 demonstrateDeepCopy(); return 0; }七、C 移动构造和移动赋值C11引入1. 基本概念移动语义允许资源的所有权从一个对象转移到另一个对象而不进行深拷贝从而提高性能。移动构造函数创建新对象时从临时对象偷取资源移动赋值运算符给已存在的对象赋值时从临时对象偷取资源2. 为什么需要移动语义// 传统深拷贝低效 vectorint createVector() { vectorint v(1000000, 42); return v; // C11之前会调用拷贝构造函数深拷贝 } // 移动语义高效 vectorint createVector() { vectorint v(1000000, 42); return v; // C11调用移动构造函数浅拷贝指针 }3. 左值、右值和右值引用int a 10; // a 是左值有名字可以取地址 int b 20; // 20 是右值临时值b 是右值引用 // 右值引用只能绑定到右值 int ref1 10; // OK10是右值 int ref2 a; // 错误a是左值 int ref3 std::move(a); // OKstd::move将左值转为右值4. 完整示例带移动语义的String类#define _CRT_SECURE_NO_WARNINGS #include iostream #include cstring #include cassert using namespace std; class Mystring { private: char* str; size_t length; // 添加长度成员便于演示 public: // 构造函数 Mystring(const char* ps nullptr) : str(nullptr), length(0) { cout 构造函数: this endl; if (ps ! nullptr *ps ! \0) { length strlen(ps); str new char[length 1]; assert(str ! nullptr); strcpy(str, ps); } } // 析构函数 ~Mystring() { cout 析构函数: this - 释放: (str ? str : null) endl; delete[] str; str nullptr; length 0; } // 拷贝构造函数深拷贝 Mystring(const Mystring other) : str(nullptr), length(other.length) { cout 拷贝构造函数: this 从 other endl; if (other.str ! nullptr) { str new char[length 1]; assert(str ! nullptr); strcpy(str, other.str); } } // 移动构造函数偷资源 Mystring(Mystring other) noexcept : str(other.str), length(other.length) { cout 移动构造函数: this 从 other endl; // 将源对象置为空防止析构时释放资源 other.str nullptr; other.length 0; } // 拷贝赋值运算符深拷贝 Mystring operator(const Mystring other) { cout 拷贝赋值: this 从 other endl; if (this ! other) { // 自赋值检查 delete[] str; // 释放原有资源 length other.length; if (other.str ! nullptr) { str new char[length 1]; assert(str ! nullptr); strcpy(str, other.str); } else { str nullptr; } } return *this; } // 移动赋值运算符偷资源 Mystring operator(Mystring other) noexcept { cout 移动赋值: this 从 other endl; if (this ! other) { // 自赋值检查 delete[] str; // 释放原有资源 // 偷取源对象的资源 str other.str; length other.length; // 将源对象置为空 other.str nullptr; other.length 0; } return *this; } void PrintInfo() const { if (str ! nullptr) { cout str: str (长度: length ) endl; } else { cout str为空 endl; } } // 获取地址用于演示 const char* getStr() const { return str; } }; // 工厂函数返回临时对象 Mystring createString(const char* s) { Mystring temp(s); cout createString 内部: ; temp.PrintInfo(); return temp; // 这里会调用移动构造函数C11优化 } int main() { cout 1. 移动构造函数演示 endl; { Mystring s1(Hello); cout s1: ; s1.PrintInfo(); // 使用std::move将左值转为右值调用移动构造函数 Mystring s2 std::move(s1); cout 移动后 s1: ; s1.PrintInfo(); // s1现在为空资源被偷走 cout 移动后 s2: ; s2.PrintInfo(); // s2拥有资源 } cout \n 2. 移动赋值演示 endl; { Mystring s1(World); Mystring s2(Hello); cout 初始状态: endl; cout s1: ; s1.PrintInfo(); cout s2: ; s2.PrintInfo(); s2 std::move(s1); // 移动赋值 cout 移动赋值后: endl; cout s1: ; s1.PrintInfo(); // s1为空 cout s2: ; s2.PrintInfo(); // s2拥有World } cout \n 3. 函数返回临时对象自动使用移动语义 endl; { Mystring s3 createString(Temporary); cout s3: ; s3.PrintInfo(); } cout \n 4. 拷贝 vs 移动对比 endl; { Mystring a(Copy me); Mystring b(a); // 拷贝构造a是左值 Mystring c(std::move(a)); // 移动构造a被转为右值 cout a: ; a.PrintInfo(); // a为空 cout b: ; b.PrintInfo(); // b有资源拷贝 cout c: ; c.PrintInfo(); // c有资源移动 } cout \n 5. 容器中使用移动语义 endl; { Mystring arr[3] {One, Two, Three}; cout 原始数组: endl; for (int i 0; i 3; i) { cout arr[ i ]: arr[i].getStr() endl; } // 移动数组中的元素 Mystring temp std::move(arr[1]); cout 移动 arr[1] 后: endl; cout arr[1]: arr[1].getStr() endl; // nullptr cout temp: temp.getStr() endl; // Two } return 0; }