类是C++面向对象编程的基本构建块,它将数据和操作数据的函数封装在一起。
1class 类名 {
2 // 访问修饰符
3private:
4 // 私有成员(默认)
5
6protected:
7 // 受保护成员
8
9public:
10 // 公有成员
11};
数据成员(属性)
xxxxxxxxxx
61class Student {
2private:
3 string name; // 字符串类型的数据成员
4 int age; // 整型数据成员
5 double score; // 浮点型数据成员
6};
成员函数(方法)
xxxxxxxxxx
211class Rectangle {
2private:
3 double width;
4 double height;
5
6public:
7 // 构造函数
8 Rectangle(double w, double h) {
9 width = w;
10 height = h;
11 }
12
13 // 成员函数
14 double getArea() {
15 return width * height;
16 }
17
18 double getPerimeter() {
19 return 2 * (width + height);
20 }
21};
C++提供了三种访问修饰符来控制类成员的访问权限:
private:私有成员,只能在类内部访问(默认)
protected:受保护成员,可在类内部和派生类中访问
public:公有成员,可在任何地方访问
xxxxxxxxxx
141class Person {
2private:
3 string idNumber; // 私有,只能在类内部访问
4
5protected:
6 string name; // 受保护,可在派生类中访问
7
8public:
9 int age; // 公有,可在任何地方访问
10
11 void setIdNumber(string id) {
12 idNumber = id; // 可以访问私有成员
13 }
14};
对象的创建是面向对象编程的基础操作。
栈上创建的对象会在超出作用域时自动销毁。
x1class MyClass {
2public:
3 MyClass() { cout << "构造函数调用" << endl; }
4 ~MyClass() { cout << "析构函数调用" << endl; }
5};
6
7void func() {
8 MyClass obj; // 在栈上创建对象
9 // 函数结束时,obj自动销毁
10}
堆上创建的对象需要手动释放内存。
xxxxxxxxxx
31MyClass* ptr = new MyClass(); // 在堆上创建对象
2// 使用对象...
3delete ptr; // 手动释放内存
默认构造函数
xxxxxxxxxx
71class Point {
2public:
3 int x, y;
4 Point() { x = 0; y = 0; } // 默认构造函数
5};
6
7Point p1; // 调用默认构造函数
带参数的构造函数
xxxxxxxxxx
101class Rectangle {
2public:
3 double width, height;
4 Rectangle(double w, double h) {
5 width = w;
6 height = h;
7 }
8};
9
10Rectangle rect(5.0, 3.0); // 调用带参数的构造函数
初始化列表
xxxxxxxxxx
91class Person {
2private:
3 string name;
4 int age;
5public:
6 Person(string n, int a) : name(n), age(a) {} // 使用初始化列表
7};
8
9Person person("张三", 25);
拷贝构造
xxxxxxxxxx
221class MyString {
2private:
3 char* data;
4public:
5 // 构造函数
6 MyString(const char* str) {
7 data = new char[strlen(str) + 1];
8 strcpy(data, str);
9 }
10
11 // 拷贝构造函数
12 MyString(const MyString& other) {
13 data = new char[strlen(other.data) + 1];
14 strcpy(data, other.data);
15 }
16
17 ~MyString() { delete[] data; }
18};
19
20MyString s1("Hello");
21MyString s2 = s1; // 调用拷贝构造函数
22MyString s3(s1); // 也是调用拷贝构造函数
移动构造(C++11)
xxxxxxxxxx
251class Vector {
2private:
3 int* data;
4 size_t size;
5public:
6 // 构造函数
7 Vector(size_t n) : size(n) {
8 data = new int[size];
9 }
10
11 // 移动构造函数
12 Vector(Vector&& other) noexcept : data(other.data), size(other.size) {
13 other.data = nullptr;
14 other.size = 0;
15 }
16
17 ~Vector() { delete[] data; }
18};
19
20Vector createVector() {
21 Vector temp(100);
22 return temp; // 返回时可能触发移动构造
23}
24
25Vector v = createVector(); // 使用移动构造函数
列表初始化(C++11)
xxxxxxxxxx
81class Point {
2public:
3 int x, y;
4 Point(int x_val, int y_val) : x(x_val), y(y_val) {}
5};
6
7Point p1{10, 20}; // 列表初始化
8Point p2 = {30, 40}; // 也是列表初始化
特性 | 栈上创建(静态) | 堆上创建(动态) |
---|---|---|
内存管理 | 自动 | 手动 |
生命周期 | 作用域结束时销毁 | 由程序员控制 |
大小限制 | 受栈大小限制 | 受可用内存限制 |
性能 | 较快 | 较慢 |
适用场景 | 小对象,临时对象 | 大对象,长生命周期对象 |
xxxxxxxxxx
61// 栈上创建对象数组
2MyClass objArray[5]; // 调用默认构造函数5次
3
4// 堆上创建对象数组
5MyClass* ptrArray = new MyClass[5]; // 调用默认构造函数5次
6delete[] ptrArray; // 释放数组内存
对象的销毁是C++内存管理的重要环节,与对象创建相对应。
栈上创建的对象会在超出其作用域时自动销毁。
xxxxxxxxxx
41void func() {
2 MyClass obj; // 在栈上创建对象
3 // 函数结束时,obj自动销毁,调用析构函数
4} // 作用域结束,obj被销毁
堆上创建的对象需要手动释放内存。
xxxxxxxxxx
41MyClass* ptr = new MyClass(); // 在堆上创建对象
2// 使用对象...
3delete ptr; // 手动释放内存,调用析构函数
4ptr = nullptr; // 良好习惯:删除后将指针置空
析构函数是对象销毁时自动调用的特殊成员函数,用于释放资源。
xxxxxxxxxx
151class ResourceManager {
2private:
3 int* data;
4
5public:
6 ResourceManager() {
7 data = new int[100]; // 分配资源
8 cout << "资源分配" << endl;
9 }
10
11 ~ResourceManager() {
12 delete[] data; // 释放资源
13 cout << "资源释放" << endl;
14 }
15};
局部对象
局部对象的销毁顺序与创建顺序相反(后进先出)。
xxxxxxxxxx
51void func() {
2 MyClass obj1; // 首先创建
3 MyClass obj2; // 然后创建
4 // obj2先销毁,然后obj1销毁
5}
成员对象
类的成员对象销毁顺序与初始化顺序相反,与声明顺序无关。
xxxxxxxxxx
91class Container {
2private:
3 ResourceA a; // 首先初始化
4 ResourceB b; // 然后初始化
5
6public:
7 Container() {}
8 ~Container() {} // b先销毁,然后a销毁
9};
继承关系中的对象
派生类对象销毁时,先调用派生类的析构函数,再调用基类的析构函数。
xxxxxxxxxx
111class Base {
2public:
3 ~Base() { cout << "Base析构" << endl; }
4};
5
6class Derived : public Base {
7public:
8 ~Derived() { cout << "Derived析构" << endl; }
9};
10
11// 销毁Derived对象时,先调用Derived析构函数,再调用Base析构函数
当通过基类指针删除派生类对象时,需要虚析构函数确保正确调用派生类的析构函数。
xxxxxxxxxx
191class Base {
2public:
3 virtual ~Base() { cout << "Base析构" << endl; }
4};
5
6class Derived : public Base {
7private:
8 int* resource;
9
10public:
11 Derived() { resource = new int[10]; }
12 ~Derived() override {
13 delete[] resource;
14 cout << "Derived析构" << endl;
15 }
16};
17
18Base* ptr = new Derived();
19delete ptr; // 正确调用Derived的析构函数,然后调用Base的析构函数
xxxxxxxxxx
81// 栈上对象数组的销毁
2{
3 MyClass objArray[5]; // 创建5个对象
4} // 作用域结束,按照创建的相反顺序销毁5个对象
5
6// 堆上对象数组的销毁
7MyClass* ptrArray = new MyClass[5];
8delete[] ptrArray; // 必须使用delete[],而不是delete
拷贝构造函数是C++中一种特殊的构造函数,用于创建一个对象的副本。它在对象需要复制时被调用,是C++中实现对象复制的重要机制。其形式为:
xxxxxxxxxx
11ClassName(const ClassName& other);
其中other
是同类型的另一个对象的引用,通常声明为常量引用。
拷贝构造函数在以下情况下会被调用:
用一个对象初始化另一个对象
xxxxxxxxxx
31MyClass obj1;
2MyClass obj2 = obj1; // 调用拷贝构造函数
3MyClass obj3(obj1); // 也调用拷贝构造函数
函数按值传递对象参数
xxxxxxxxxx
61void func(MyClass obj) { // 调用拷贝构造函数
2 // 函数体
3}
4
5MyClass obj;
6func(obj); // obj被复制
函数按值返回对象
xxxxxxxxxx
41MyClass createObject() {
2 MyClass obj;
3 return obj; // 调用拷贝构造函数(可能被优化)
4}
如果没有显式定义拷贝构造函数,编译器会提供一个默认的拷贝构造函数,执行浅拷贝(逐成员复制)。
xxxxxxxxxx
71class Simple {
2public:
3 int value;
4 string name;
5 // 编译器生成的默认拷贝构造函数大致相当于:
6 // Simple(const Simple& other) : value(other.value), name(other.name) {}
7};
当类包含动态分配的资源时,需要自定义拷贝构造函数实现深拷贝。
xxxxxxxxxx
281class DynamicArray {
2private:
3 int* data;
4 int size;
5
6public:
7 // 普通构造函数
8 DynamicArray(int sz) {
9 size = sz;
10 data = new int[size];
11 }
12
13 // 拷贝构造函数(深拷贝)
14 DynamicArray(const DynamicArray& other) {
15 size = other.size;
16 data = new int[size]; // 分配新内存
17
18 // 复制数据
19 for (int i = 0; i < size; i++) {
20 data[i] = other.data[i];
21 }
22 }
23
24 // 析构函数
25 ~DynamicArray() {
26 delete[] data;
27 }
28};
浅拷贝
简单地复制成员变量的值
对于指针成员,只复制指针值,不复制指针指向的内容
可能导致多个对象共享同一资源,引发"双重释放"问题
深拷贝
不仅复制成员变量的值,还为指针成员分配新内存并复制内容
确保每个对象拥有自己独立的资源
避免资源共享导致的问题
xxxxxxxxxx
41MyClass obj1;
2MyClass obj2 = obj1; // 拷贝构造函数(初始化)
3MyClass obj3;
4obj3 = obj1; // 赋值运算符(赋值)
拷贝构造函数:创建新对象时调用
赋值运算符:已存在的对象间赋值时调用
在某些情况下,可能需要禁止对象的复制:
xxxxxxxxxx
121class Uncopyable {
2public:
3 Uncopyable() {}
4
5 // C++98/03方式:声明为私有但不定义
6private:
7 Uncopyable(const Uncopyable&);
8
9 // C++11方式:使用delete关键字
10public:
11 Uncopyable(const Uncopyable&) = delete;
12};
赋值运算符函数是C++中的一种特殊成员函数,用于定义对象之间的赋值行为。它在已经存在的对象之间进行赋值操作时被调用。
赋值运算符函数的形式为:
xxxxxxxxxx
11ClassName& operator=(const ClassName& other);
其中other
是要赋值的源对象,返回类型通常是当前类的引用,以支持连续赋值。
如果没有显式定义赋值运算符,编译器会提供一个默认的赋值运算符,执行浅拷贝(逐成员复制)。
xxxxxxxxxx
111class Simple {
2public:
3 int value;
4 string name;
5 // 编译器生成的默认赋值运算符大致相当于:
6 // Simple& operator=(const Simple& other) {
7 // value = other.value;
8 // name = other.name;
9 // return *this;
10 // }
11};
当类包含动态分配的资源时,需要自定义赋值运算符实现深拷贝。
xxxxxxxxxx
401class DynamicArray {
2private:
3 int* data;
4 int size;
5
6public:
7 // 构造函数
8 DynamicArray(int sz) {
9 size = sz;
10 data = new int[size];
11 }
12
13 // 析构函数
14 ~DynamicArray() {
15 delete[] data;
16 }
17
18 // 赋值运算符(深拷贝)
19 DynamicArray& operator=(const DynamicArray& other) {
20 // 自我赋值检查
21 if (this == &other) {
22 return *this;
23 }
24
25 // 释放原有资源
26 delete[] data;
27
28 // 分配新资源
29 size = other.size;
30 data = new int[size];
31
32 // 复制数据
33 for (int i = 0; i < size; i++) {
34 data[i] = other.data[i];
35 }
36
37 // 返回当前对象的引用
38 return *this;
39 }
40};
自我赋值检查
防止对象赋值给自己时出现问题:
xxxxxxxxxx
31if (this == &other) {
2 return *this;
3}
异常安全
确保在分配新资源失败时不会丢失原有资源:
xxxxxxxxxx
151// 异常安全的赋值运算符
2DynamicArray& operator=(const DynamicArray& other) {
3 if (this != &other) {
4 int* newData = new int[other.size]; // 先分配新内存
5
6 delete[] data; // 释放旧内存
7 data = newData; // 更新指针
8 size = other.size;
9
10 for (int i = 0; i < size; i++) {
11 data[i] = other.data[i];
12 }
13 }
14 return *this;
15}
返回*this
返回对象自身的引用,支持连续赋值:
xxxxxxxxxx
11a = b = c; // 等价于 a = (b = c);
赋值运算符:用于已存在对象之间的赋值
xxxxxxxxxx
21MyClass a, b;
2a = b; // 调用赋值运算符
拷贝构造函数:用于创建新对象时的初始化
xxxxxxxxxx
21MyClass a;
2MyClass b = a; // 调用拷贝构造函数
在某些情况下,可能需要禁止对象的赋值:
xxxxxxxxxx
121class Uncopyable {
2public:
3 Uncopyable() {}
4
5 // C++98/03方式:声明为私有但不定义
6private:
7 Uncopyable& operator=(const Uncopyable&);
8
9 // C++11方式:使用delete关键字
10public:
11 Uncopyable& operator=(const Uncopyable&) = delete;
12};
C++类中有几种特殊的数据成员,它们具有特殊的性质和用途。
静态数据成员是属于类而非对象的成员变量,所有对象共享同一个静态数据成员。
xxxxxxxxxx
111class Counter {
2private:
3 static int count; // 静态数据成员声明
4
5public:
6 Counter() { count++; }
7 static int getCount() { return count; }
8};
9
10// 必须在类外定义和初始化静态数据成员
11int Counter::count = 0;
特点:
在类的所有对象间共享
必须在类外定义和初始化
可以通过类名直接访问:Counter::getCount()
存在于程序的整个生命周期
常量数据成员是在对象创建后不能修改的成员变量。
xxxxxxxxxx
131class Circle {
2private:
3 const double PI; // 常量数据成员
4 double radius;
5
6public:
7 // 常量成员必须在初始化列表中初始化
8 Circle(double r) : PI(3.14159), radius(r) {}
9
10 double getArea() const {
11 return PI * radius * radius;
12 }
13};
特点:
必须在构造函数的初始化列表中初始化
初始化后不能修改
每个对象可以有不同的常量值
引用数据成员是对其他对象的引用。
xxxxxxxxxx
111class Wrapper {
2private:
3 int& ref; // 引用数据成员
4
5public:
6 // 引用成员必须在初始化列表中初始化
7 Wrapper(int& r) : ref(r) {}
8
9 void increment() { ref++; }
10 int getValue() const { return ref; }
11};
特点:
必须在构造函数的初始化列表中初始化
初始化后不能重新绑定到其他对象
对引用成员的修改会影响被引用的对象
静态成员函数是属于类而非对象的函数,它们可以在不创建类的实例的情况下被调用。
特点
不依赖对象实例:静态成员函数不与任何对象关联,可以通过类名直接调用
没有this指针:由于不依赖对象实例,静态成员函数没有this指针
只能访问静态成员:静态成员函数只能访问类的静态成员(静态数据成员和其他静态成员函数)
不能声明为const:静态成员函数不能被声明为const、volatile或virtual
不能使用非静态数据成员:不能直接访问类的非静态数据成员
语法
xxxxxxxxxx
311class MyClass {
2private:
3 static int count; // 静态数据成员
4 int value; // 非静态数据成员
5
6public:
7 // 静态成员函数
8 static int getCount() {
9 return count; // 可以访问静态数据成员
10 // return value; // 错误!不能访问非静态数据成员
11 }
12
13 // 普通成员函数
14 void increment() {
15 value++;
16 count++; // 普通成员函数可以访问静态数据成员
17 }
18};
19
20// 静态数据成员必须在类外定义
21int MyClass::count = 0;
22
23// 调用静态成员函数
24int main() {
25 int c1 = MyClass::getCount(); // 通过类名调用
26
27 MyClass obj;
28 int c2 = obj.getCount(); // 也可以通过对象调用(不推荐)
29
30 return 0;
31}
应用场景
计数器:记录类创建了多少个实例
工厂方法:创建类的实例
单例模式:确保类只有一个实例
工具函数:提供与类相关但不需要对象状态的功能
const成员函数是承诺不会修改对象状态的成员函数,通过在函数声明后加const关键字实现。
特点
不能修改对象状态:const成员函数不能修改非mutable的数据成员
可以用于const对象:const对象只能调用const成员函数
this指针为const:在const成员函数中,this指针是指向const对象的指针
可以重载:可以基于const限定符重载成员函数
可以修改mutable成员:即使在const成员函数中,也可以修改声明为mutable的成员
语法
xxxxxxxxxx
451class Rectangle {
2private:
3 double width;
4 double height;
5 mutable int accessCount; // 可变成员
6
7public:
8 Rectangle(double w, double h) : width(w), height(h), accessCount(0) {}
9
10 // const成员函数
11 double getArea() const {
12 accessCount++; // 可以修改mutable成员
13 return width * height;
14 // width = 10; // 错误!不能修改非mutable成员
15 }
16
17 // 非const成员函数
18 void resize(double w, double h) {
19 width = w;
20 height = h;
21 }
22
23 // 基于const重载
24 void print() const {
25 std::cout << "Const版本: " << width << " x " << height << std::endl;
26 }
27
28 void print() {
29 std::cout << "非Const版本: " << width << " x " << height << std::endl;
30 }
31};
32
33int main() {
34 Rectangle r(5, 3);
35 r.getArea(); // 可以调用const成员函数
36 r.resize(10, 6); // 可以调用非const成员函数
37 r.print(); // 调用非const版本
38
39 const Rectangle cr(2, 4);
40 cr.getArea(); // 可以调用const成员函数
41 // cr.resize(3, 5); // 错误!const对象不能调用非const成员函数
42 cr.print(); // 调用const版本
43
44 return 0;
45}
应用场景
访问器函数:获取对象状态但不修改它
查询操作:执行不改变对象状态的操作
const对象的方法:允许const对象调用的方法
线程安全:在多线程环境中表明函数不会修改共享数据
特性 | 静态成员函数 | const成员函数 |
---|---|---|
调用方式 | 通过类名或对象 | 只能通过对象 |
this指针 | 没有 | const this指针 |
访问限制 | 只能访问静态成员 | 可以访问所有成员,但不能修改非mutable成员 |
对象要求 | 不需要对象实例 | 可以被const和非const对象调用 |
主要用途 | 与类相关但不依赖对象状态的功能 | 不修改对象状态的操作 |
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。
单例模式的核心思想是:
确保类只有一个实例
提供全局访问该实例的方法
控制实例的创建时机
xxxxxxxxxx
291class Singleton {
2private:
3 // 私有构造函数,防止外部创建实例
4 Singleton() {}
5
6 // 禁用拷贝构造和赋值操作
7 Singleton(const Singleton&) = delete;
8 Singleton& operator=(const Singleton&) = delete;
9
10 // 静态实例指针
11 static Singleton* instance;
12
13public:
14 // 获取实例的静态方法
15 static Singleton* getInstance() {
16 if (instance == nullptr) {
17 instance = new Singleton();
18 }
19 return instance;
20 }
21
22 // 业务方法
23 void doSomething() {
24 // 实际功能
25 }
26};
27
28// 静态成员初始化
29Singleton* Singleton::instance = nullptr;
特点:
第一次调用getInstance()
时才创建实例
存在线程安全问题
需要手动管理内存(可能导致内存泄漏)
xxxxxxxxxx
261class Singleton {
2private:
3 // 私有构造函数
4 Singleton() {}
5
6 // 禁用拷贝构造和赋值操作
7 Singleton(const Singleton&) = delete;
8 Singleton& operator=(const Singleton&) = delete;
9
10 // 静态实例(程序启动时创建)
11 static Singleton instance;
12
13public:
14 // 获取实例的静态方法
15 static Singleton& getInstance() {
16 return instance;
17 }
18
19 // 业务方法
20 void doSomething() {
21 // 实际功能
22 }
23};
24
25// 静态成员初始化
26Singleton Singleton::instance;
特点:
程序启动时就创建实例
线程安全
不需要考虑内存释放问题
可能造成资源浪费(如果该实例从未被使用)
xxxxxxxxxx
361
2
3class Singleton {
4private:
5 // 私有构造函数
6 Singleton() {}
7
8 // 禁用拷贝构造和赋值操作
9 Singleton(const Singleton&) = delete;
10 Singleton& operator=(const Singleton&) = delete;
11
12 // 静态实例指针
13 static Singleton* instance;
14 static std::mutex mutex;
15
16public:
17 // 获取实例的静态方法(双检锁)
18 static Singleton* getInstance() {
19 if (instance == nullptr) { // 第一次检查
20 std::lock_guard<std::mutex> lock(mutex);
21 if (instance == nullptr) { // 第二次检查
22 instance = new Singleton();
23 }
24 }
25 return instance;
26 }
27
28 // 业务方法
29 void doSomething() {
30 // 实际功能
31 }
32};
33
34// 静态成员初始化
35Singleton* Singleton::instance = nullptr;
36std::mutex Singleton::mutex;
特点:
线程安全
延迟初始化
双检锁提高性能
仍然存在内存泄漏问题
xxxxxxxxxx
221class Singleton {
2private:
3 // 私有构造函数
4 Singleton() {}
5
6 // 禁用拷贝构造和赋值操作
7 Singleton(const Singleton&) = delete;
8 Singleton& operator=(const Singleton&) = delete;
9
10public:
11 // 获取实例的静态方法
12 static Singleton& getInstance() {
13 // 局部静态变量
14 static Singleton instance;
15 return instance;
16 }
17
18 // 业务方法
19 void doSomething() {
20 // 实际功能
21 }
22};
特点:
C++11保证了局部静态变量的线程安全初始化
简洁易用
自动管理内存(不会内存泄漏)
延迟初始化
xxxxxxxxxx
181
2
3class Singleton {
4private:
5 Singleton() {}
6
7 static std::unique_ptr<Singleton> instance;
8
9public:
10 static Singleton& getInstance() {
11 if (!instance) {
12 instance.reset(new Singleton());
13 }
14 return *instance;
15 }
16};
17
18std::unique_ptr<Singleton> Singleton::instance = nullptr;
xxxxxxxxxx
311class Singleton {
2private:
3 Singleton() {}
4
5 static Singleton* instance;
6
7 // 嵌套类作为析构器
8 class Destructor {
9 public:
10 ~Destructor() {
11 if (Singleton::instance) {
12 delete Singleton::instance;
13 Singleton::instance = nullptr;
14 }
15 }
16 };
17
18 // 静态析构器对象
19 static Destructor destructor;
20
21public:
22 static Singleton* getInstance() {
23 if (instance == nullptr) {
24 instance = new Singleton();
25 }
26 return instance;
27 }
28};
29
30Singleton* Singleton::instance = nullptr;
31Singleton::Destructor Singleton::destructor;
优点:
保证一个类只有一个实例
提供全局访问点
实例在第一次使用时才被创建(懒汉式)
避免频繁创建和销毁实例
缺点:
单例模式违反了单一职责原则
隐藏了类之间的依赖关系
不利于单元测试
多线程环境下需要特别注意线程安全
C++风格字符串是指C++标准库中的std::string
类,它是对C风格字符串的封装和扩展,提供了更安全、更方便的字符串处理方式。
std::string
是C++标准库中的字符串类,定义在<string>
头文件中,属于std
命名空间。
xxxxxxxxxx
81
2
3
4int main() {
5 std::string str = "Hello, C++";
6 std::cout << str << std::endl;
7 return 0;
8}
xxxxxxxxxx
161// 默认构造函数(空字符串)
2std::string s1;
3
4// 使用C风格字符串初始化
5std::string s2 = "Hello";
6std::string s3("World");
7
8// 使用另一个string初始化
9std::string s4 = s2;
10std::string s5(s3);
11
12// 使用部分字符串初始化
13std::string s6(s2, 1, 3); // 从s2的索引1开始,长度为3:"ell"
14
15// 重复字符初始化
16std::string s7(5, 'A'); // "AAAAA"
字符串连接
xxxxxxxxxx
81std::string first = "Hello";
2std::string last = "World";
3
4// 使用+运算符
5std::string message = first + " " + last; // "Hello World"
6
7// 使用append方法
8first.append(" ").append(last); // first变为"Hello World"
访问字符
xxxxxxxxxx
111std::string str = "Hello";
2
3// 使用[]运算符(不进行边界检查)
4char c1 = str[0]; // 'H'
5
6// 使用at方法(进行边界检查,越界抛出异常)
7char c2 = str.at(1); // 'e'
8
9// 获取第一个和最后一个字符
10char first = str.front(); // 'H'
11char last = str.back(); // 'o'
字符串长度和容量
xxxxxxxxxx
131std::string str = "Hello, World";
2
3// 获取长度
4size_t length = str.length(); // 12
5size_t size = str.size(); // 12(与length相同)
6
7// 检查是否为空
8bool isEmpty = str.empty(); // false
9
10// 容量管理
11size_t capacity = str.capacity(); // 返回当前分配的存储空间大小
12str.reserve(100); // 预分配空间
13str.shrink_to_fit(); // 减少容量以适应实际大小
子字符串
xxxxxxxxxx
41std::string str = "Hello, World";
2
3// 提取子字符串
4std::string sub = str.substr(7, 5); // "World"(从索引7开始,长度为5)
查找和替换
xxxxxxxxxx
131std::string str = "Hello, World";
2
3// 查找
4size_t pos1 = str.find("World"); // 返回7
5size_t pos2 = str.find('o'); // 返回4(第一个'o'的位置)
6size_t pos3 = str.rfind('o'); // 返回8(最后一个'o'的位置)
7
8// 查找任意字符
9size_t pos4 = str.find_first_of("aeiou"); // 返回1(第一个元音字母'e'的位置)
10size_t pos5 = str.find_last_of("aeiou"); // 返回8(最后一个元音字母'o'的位置)
11
12// 替换
13str.replace(7, 5, "C++"); // "Hello, C++"(从索引7开始,替换5个字符)
插入和删除
xxxxxxxxxx
71std::string str = "Hello";
2
3// 插入
4str.insert(5, " World"); // "Hello World"
5
6// 删除
7str.erase(5, 6); // "Hello"(从索引5开始删除6个字符)
比较
xxxxxxxxxx
91std::string s1 = "apple";
2std::string s2 = "banana";
3
4// 使用比较运算符
5bool less = s1 < s2; // true
6bool equal = s1 == "apple"; // true
7
8// 使用compare方法
9int result = s1.compare(s2); // 负值,表示s1小于s2
C++字符串转C风格字符串
xxxxxxxxxx
71std::string cppStr = "Hello";
2
3// 获取C风格字符串(只读)
4const char* cStr = cppStr.c_str();
5
6// 获取字符数组(只读)
7const char* data = cppStr.data();
C风格字符串转C++字符串
xxxxxxxxxx
21const char* cStr = "Hello";
2std::string cppStr(cStr);
xxxxxxxxxx
121
2
3// 字符串构建
4std::ostringstream oss;
5oss << "Name: " << "John" << ", Age: " << 30;
6std::string result = oss.str(); // "Name: John, Age: 30"
7
8// 字符串解析
9std::string input = "123 456";
10std::istringstream iss(input);
11int a, b;
12iss >> a >> b; // a=123, b=456
xxxxxxxxxx
81// 字符串转数字
2std::string numStr = "123";
3int num = std::stoi(numStr); // 字符串转int
4double dbl = std::stod("3.14159"); // 字符串转double
5
6// 数字转字符串
7std::string s1 = std::to_string(123); // 整数转字符串
8std::string s2 = std::to_string(3.14159); // 浮点数转字符串
特性 | std::string | C风格字符串 |
---|---|---|
内存管理 | 自动 | 手动 |
边界检查 | 支持(at方法) | 不支持 |
动态调整大小 | 支持 | 不支持 |
字符串操作 | 丰富的成员函数 | 需要使用库函数 |
连接操作 | 使用+运算符 | 需要使用strcat等函数 |
安全性 | 高 | 低(容易缓冲区溢出) |
std::vector
是C++标准模板库(STL)中最常用的容器之一,它实现了动态数组的功能,能够自动管理内存并提供丰富的操作接口。
std::vector
是一个模板类,定义在<vector>
头文件中,属于std
命名空间。它提供了一个可以动态增长的数组,具有以下特点:
元素在内存中连续存储
支持随机访问
在末尾添加/删除元素的时间复杂度为均摊O(1)
在中间插入/删除元素的时间复杂度为O(n)
自动管理内存
xxxxxxxxxx
211
2
3// 创建空vector
4std::vector<int> vec1;
5
6// 创建指定大小的vector,所有元素初始化为默认值
7std::vector<int> vec2(5); // 包含5个值为0的整数
8
9// 创建指定大小的vector,所有元素初始化为指定值
10std::vector<int> vec3(5, 10); // 包含5个值为10的整数
11
12// 使用初始化列表(C++11)
13std::vector<int> vec4 = {1, 2, 3, 4, 5};
14std::vector<int> vec5{1, 2, 3, 4, 5}; // 与上面等价
15
16// 使用另一个vector初始化
17std::vector<int> vec6(vec4); // 复制vec4的所有元素
18std::vector<int> vec7 = vec4; // 与上面等价
19
20// 使用迭代器范围初始化
21std::vector<int> vec8(vec4.begin(), vec4.begin() + 3); // 包含vec4的前3个元素
添加和删除元素
xxxxxxxxxx
271std::vector<int> vec;
2
3// 在末尾添加元素
4vec.push_back(10);
5vec.push_back(20);
6
7// 在指定位置插入元素
8vec.insert(vec.begin() + 1, 15); // 在第二个位置插入15
9
10// 在指定位置插入多个相同的元素
11vec.insert(vec.begin(), 3, 5); // 在开头插入3个5
12
13// 在指定位置插入一个范围的元素
14std::vector<int> another = {100, 200, 300};
15vec.insert(vec.end(), another.begin(), another.end());
16
17// 删除末尾元素
18vec.pop_back();
19
20// 删除指定位置的元素
21vec.erase(vec.begin() + 2); // 删除第三个元素
22
23// 删除一个范围的元素
24vec.erase(vec.begin(), vec.begin() + 3); // 删除前3个元素
25
26// 清空vector
27vec.clear();
访问元素
xxxxxxxxxx
141std::vector<int> vec = {10, 20, 30, 40, 50};
2
3// 使用下标访问(不进行边界检查)
4int a = vec[2]; // 获取第3个元素: 30
5
6// 使用at方法(进行边界检查,越界抛出std::out_of_range异常)
7int b = vec.at(3); // 获取第4个元素: 40
8
9// 获取第一个和最后一个元素
10int first = vec.front(); // 10
11int last = vec.back(); // 50
12
13// 获取原始指针
14int* data = vec.data(); // 指向内部数组的指针
大小和容量
xxxxxxxxxx
201std::vector<int> vec = {1, 2, 3, 4, 5};
2
3// 获取元素个数
4size_t size = vec.size(); // 5
5
6// 检查是否为空
7bool empty = vec.empty(); // false
8
9// 获取当前容量(分配的内存可以容纳的元素数量)
10size_t capacity = vec.capacity(); // 可能大于或等于5
11
12// 调整大小
13vec.resize(10); // 扩大到10个元素,新元素用默认值初始化
14vec.resize(3); // 缩小到3个元素,多余的元素被删除
15
16// 预留空间(不创建新元素)
17vec.reserve(100); // 确保容量至少为100
18
19// 减少容量以适应实际大小
20vec.shrink_to_fit();
xxxxxxxxxx
261std::vector<int> vec = {10, 20, 30, 40, 50};
2
3// 使用迭代器遍历
4for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
5 std::cout << *it << " ";
6}
7
8// 使用const迭代器遍历
9for (std::vector<int>::const_iterator it = vec.cbegin(); it != vec.cend(); ++it) {
10 std::cout << *it << " ";
11}
12
13// 使用反向迭代器遍历(从后向前)
14for (std::vector<int>::reverse_iterator it = vec.rbegin(); it != vec.rend(); ++it) {
15 std::cout << *it << " ";
16}
17
18// C++11的范围for循环
19for (int val : vec) {
20 std::cout << val << " ";
21}
22
23// C++11的auto关键字简化迭代器声明
24for (auto it = vec.begin(); it != vec.end(); ++it) {
25 std::cout << *it << " ";
26}
std::vector
可以与STL算法库一起使用:
xxxxxxxxxx
341
2
3
4
5std::vector<int> vec = {5, 2, 8, 1, 9};
6
7// 排序
8std::sort(vec.begin(), vec.end()); // 升序排序: {1, 2, 5, 8, 9}
9std::sort(vec.begin(), vec.end(), std::greater<int>()); // 降序排序: {9, 8, 5, 2, 1}
10
11// 查找
12auto it = std::find(vec.begin(), vec.end(), 5);
13if (it != vec.end()) {
14 std::cout << "找到元素: " << *it << std::endl;
15}
16
17// 计算总和
18int sum = std::accumulate(vec.begin(), vec.end(), 0);
19
20// 最大值和最小值
21auto minmax = std::minmax_element(vec.begin(), vec.end());
22int min_val = *minmax.first;
23int max_val = *minmax.second;
24
25// 填充
26std::fill(vec.begin(), vec.end(), 10); // 所有元素设为10
27
28// 复制
29std::vector<int> vec2(vec.size());
30std::copy(vec.begin(), vec.end(), vec2.begin());
31
32// 变换
33std::transform(vec.begin(), vec.end(), vec.begin(),
34 [](int x) { return x * 2; }); // 所有元素乘以2
xxxxxxxxxx
171// 创建二维vector
2std::vector<std::vector<int>> matrix;
3
4// 初始化3x4的矩阵,所有元素为0
5std::vector<std::vector<int>> matrix2(3, std::vector<int>(4, 0));
6
7// 访问元素
8matrix2[1][2] = 5;
9int val = matrix2[1][2]; // 5
10
11// 添加一行
12std::vector<int> new_row = {1, 2, 3, 4};
13matrix2.push_back(new_row);
14
15// 获取行数和列数
16size_t rows = matrix2.size();
17size_t cols = matrix2[0].size();
特性 | std::vector | C风格数组 |
---|---|---|
大小 | 可动态调整 | 固定 |
内存管理 | 自动 | 手动 |
边界检查 | 支持(at方法) | 不支持 |
功能 | 丰富的成员函数和算法 | 基本操作 |
性能 | 略有开销 | 最高效 |
安全性 | 高 | 低 |
vector在底层通常由三个指针(或迭代器)组成:
start:指向数组中第一个元素的位置
finish:指向最后一个元素之后的位置(past-the-end)
end of storage:指向分配内存的末尾
xxxxxxxxxx
81内存布局示意图:
2┌───────────────────────────────────────────────────┐
3│ 已分配的内存 │
4├───────────────────────┬───────────────────────────┤
5│ 已使用的元素 │ 未使用的空间 │
6└───────────────────────┴───────────────────────────┘
7↑ ↑ ↑
8begin end capacity
vector使用连续的内存块来存储元素,这使得它能够提供随机访问的能力。当创建一个空的vector时,通常不会立即分配内存,而是在添加第一个元素时才分配。
当vector需要更多空间时(例如通过push_back
添加元素),它会:
分配一个更大的新内存块(通常是当前容量的1.5倍或2倍,具体倍数取决于实现)
将现有元素复制或移动到新内存
释放旧内存
更新指针
这种策略确保了push_back
操作的均摊时间复杂度为O(1)。
xxxxxxxxxx
81// 伪代码展示容量增长
2if (size == capacity) {
3 new_capacity = capacity * growth_factor; // 通常是1.5或2
4 new_memory = allocate(new_capacity);
5 copy_elements(old_memory, new_memory, size);
6 deallocate(old_memory);
7 update_pointers(new_memory);
8}
当vector被销毁或调用clear()
方法时,它会销毁所有元素并释放分配的内存。resize()
和shrink_to_fit()
也可能导致内存重新分配。
xxxxxxxxxx
831template <typename T>
2class SimpleVector {
3private:
4 T* data_; // 指向数据的指针
5 size_t size_; // 元素个数
6 size_t capacity_; // 当前分配的容量
7
8 // 重新分配内存
9 void reallocate(size_t new_capacity) {
10 // 分配新内存
11 T* new_data = new T[new_capacity];
12
13 // 复制现有元素
14 for (size_t i = 0; i < size_; ++i) {
15 new_data[i] = std::move(data_[i]); // 使用移动语义
16 }
17
18 // 释放旧内存
19 delete[] data_;
20
21 // 更新指针和容量
22 data_ = new_data;
23 capacity_ = new_capacity;
24 }
25
26public:
27 // 构造函数
28 SimpleVector() : data_(nullptr), size_(0), capacity_(0) {}
29
30 // 析构函数
31 ~SimpleVector() {
32 delete[] data_;
33 }
34
35 // 添加元素
36 void push_back(const T& value) {
37 if (size_ == capacity_) {
38 // 如果没有足够空间,增加容量
39 size_t new_capacity = capacity_ == 0 ? 1 : capacity_ * 2;
40 reallocate(new_capacity);
41 }
42
43 // 在末尾添加元素
44 data_[size_] = value;
45 ++size_;
46 }
47
48 // 访问元素
49 T& operator[](size_t index) {
50 return data_[index];
51 }
52
53 // 获取大小
54 size_t size() const {
55 return size_;
56 }
57
58 // 获取容量
59 size_t capacity() const {
60 return capacity_;
61 }
62
63 // 预留空间
64 void reserve(size_t new_capacity) {
65 if (new_capacity > capacity_) {
66 reallocate(new_capacity);
67 }
68 }
69
70 // 调整大小
71 void resize(size_t new_size) {
72 if (new_size > capacity_) {
73 reallocate(new_size);
74 }
75
76 // 如果扩大,初始化新元素
77 for (size_t i = size_; i < new_size; ++i) {
78 data_[i] = T();
79 }
80
81 size_ = new_size;
82 }
83};
迭代器失效:在修改vector时,迭代器可能会失效
xxxxxxxxxx
71std::vector<int> vec = {1, 2, 3, 4, 5};
2for (auto it = vec.begin(); it != vec.end(); ++it) {
3 if (*it == 3) {
4 vec.erase(it); // 危险!erase后迭代器失效
5 // 正确做法: it = vec.erase(it); --it;
6 }
7}
引用失效:当vector重新分配内存时,之前的引用会失效
xxxxxxxxxx
41std::vector<int> vec = {1, 2, 3};
2int& ref = vec[0]; // 获取第一个元素的引用
3vec.push_back(4); // 可能导致重新分配内存
4ref = 10; // 危险!ref可能已经失效
使用reserve避免频繁重新分配
xxxxxxxxxx
51std::vector<int> vec;
2vec.reserve(1000); // 预分配空间
3for (int i = 0; i < 1000; ++i) {
4 vec.push_back(i); // 不会导致重新分配
5}