400
社区成员
unique_ptr
、shared_ptr
和 weak_ptr
。它们分别用于不同的场景,有不同的内存管理特点。下面是它们的对比:auto_ptrC++11已经弃用
智能指针 | 所有权管理 | 引用计数 | 性能 | 优点 | 缺点 |
---|---|---|---|---|---|
unique_ptr | 独占对象的所有权 | 无 | 高性能 | - 独占所有权,确保没有多个指针指向同一个对象 - 自动释放内存,避免内存泄漏 | - 不能复制,只能移动 - 只能有一个拥有者 |
shared_ptr | 共享对象的所有权 | 有 | 性能较差(因引用计数管理) | - 可以在多个指针间共享所有权 - 引用计数机制,多个 shared_ptr 可以管理同一个对象 | - 引用计数机制开销较大 - 循环引用问题 |
weak_ptr | 解决 shared_ptr 的循环引用问题 | 无 | 性能较好 | - 用于打破 shared_ptr 之间的循环引用- 不增加引用计数,不干扰对象生命周期 | - 不能直接访问对象,需要通过 shared_ptr 转化 |
shared_ptr<Buffer> buf=make_shared<Buffer>("auto free memory");// Buffer对象分配在堆上,但能自动释放
/*
解释:
1. make_shared<Buffer>("auto free memory"):
是 C++ 标准库提供的一个函数,它创建一个 shared_ptr,并通过动态分配内存来创建 Buffer 对象。它传递给 Buffer 类构造函数一个字符串 "auto free memory",用于初始化 Buffer 对象的成员。
该函数内部会先为 Buffer 对象分配内存,然后为 shared_ptr 自身分配内存,最后将 Buffer 对象的地址交给 shared_ptr 来管理。
2. shared_ptr<Buffer> buf:
buf 是一个智能指针,类型为 shared_ptr<Buffer>。它持有 Buffer 对象的所有权,并负责在最后一个 shared_ptr 被销毁时自动释放内存。
shared_ptr 使用引用计数机制来管理内存,当没有 shared_ptr 再引用这个对象时,它会自动销毁并释放 Buffer 对象的内存。
3. 自动释放:
shared_ptr 在对象引用计数为 0 时,会自动释放内存。这意味着当 buf 超出作用域,或者 buf 被重新赋值时,Buffer 对象的内存会被自动释放。
由于 shared_ptr 管理着内存的生命周期,所以你不需要手动调用 delete 来释放 Buffer 对象。
*/
Buffer *buf = new Buffer("auto free memory");// Buffer对象分配在堆上,但需要手动delete释放
/*
1. new Buffer("auto free memory"):
这里使用 new 操作符动态分配了一个 Buffer 对象。Buffer 的构造函数接受一个字符串参数 "auto free memory",并用它初始化 Buffer 对象的成员。
new 操作符会在堆上分配内存,并返回一个指向该内存块的指针。
2. Buffer *buf:
buf 是一个指向 Buffer 对象的原生指针(Buffer*)。
你需要手动管理对象的生命周期。当不再需要该对象时,你必须手动调用 delete 来释放它所占用的内存,否则会导致内存泄漏。
*/
进程处理完之后会释放,count共享,析构会释放
shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。再最后一个shared_ptr析构的时候,内存才会被释放
shared_ptr实现包含了两部分,
1. 一个指向堆上创建的对象的裸指针,raw_ptr
2. 一个指向内部隐藏的、共享的管理对象。share_count_object 引用计数
/*
1. make_shared:
std::make_shared<Buffer>("Hello, World!") 会创建一个 shared_ptr,并在堆上动态分配 Buffer 对象。这是推荐的创建 shared_ptr 的方式,它避免了 new 的多次内存分配。
2. get:
buf1.get() 返回 shared_ptr 内部管理的原生指针,允许直接操作对象,但要小心对象的生命周期,因为 shared_ptr 的引用计数会管理对象的释放。
3.use_count:
buf1.use_count() 返回当前有多少个 shared_ptr 共享该对象的所有权。在示例中,buf1 和 buf2 共享 Buffer 对象,所以返回的值为 2。
4.reset:
buf1.reset() 会解除 buf1 对原对象的所有权,并使它指向一个新的对象(如果给定了新的对象)。当 reset 被调用后,buf1 不再管理原对象,原对象如果没有其他 shared_ptr 引用,会被销毁。
5.unique:
buf1.unique() 检查当前 shared_ptr 是否唯一指向对象。如果当前 shared_ptr 唯一指向对象(没有其他 shared_ptr 共享所有权),它返回 true;否则返回 false。
6.swap:
buf1.swap(buf3) 交换 buf1 和 buf3 的所有权,即它们各自指向的对象也交换。调用后,buf1 会指向 buf3 原来的对象,buf3 会指向 buf1 原来的对象。
*/
#include <iostream>
#include <memory>
class Buffer {
public:
explicit Buffer(const std::string& msg) : message(msg) {
std::cout << "Buffer created: " << message << std::endl;
}
~Buffer() {
std::cout << "Buffer destroyed." << std::endl;
}
void print() { std::cout << "Message: " << message << std::endl; }
private:
std::string message;
};
int main() {
// 使用 make_shared 创建 shared_ptr,推荐的创建方式
// make_shared 会在内部同时分配 memory for shared_ptr 和对象,避免两次分配
std::shared_ptr<Buffer> buf1 = std::make_shared<Buffer>("Hello, World!");
// 使用 get() 获取 shared_ptr 内部的原生指针,原生指针可以直接操作对象
Buffer* rawPtr = buf1.get(); // 获取原生指针
rawPtr->print(); // 通过原生指针访问成员函数
// 使用 use_count() 获取当前 shared_ptr 的引用计数
std::cout << "use_count: " << buf1.use_count() << std::endl; // 输出 1,因为只有 buf1 指向该对象
// 创建另一个 shared_ptr buf2,并共享 buf1 的所有权
std::shared_ptr<Buffer> buf2 = buf1;
std::cout << "use_count after buf2: " << buf1.use_count() << std::endl; // 输出 2,因为 buf1 和 buf2 都指向同一个对象
// 使用 reset() 来重置智能指针的所有权,这会解除与原对象的关联
// 重置后,buf1 将不再管理原来的对象
buf1.reset(new Buffer("New Buffer"));
// 此时 buf1 指向新对象,buf2 仍然指向原对象
buf1->print(); // 输出 "Message: New Buffer"
std::cout << "use_count after reset: " << buf1.use_count() << std::endl; // 输出 1,因为 buf1 唯一指向新对象
std::cout << "use_count of buf2: " << buf2.use_count() << std::endl; // 输出 1,buf2 仍然指向原对象
// 使用 unique() 判断 shared_ptr 是否唯一指向对象
if (buf1.unique()) {
std::cout << "buf1 is the unique owner of the object." << std::endl;
} else {
std::cout << "buf1 is not the unique owner." << std::endl;
}
// 使用 swap() 交换 buf1 和 buf3 的所有权
std::shared_ptr<Buffer> buf3 = std::make_shared<Buffer>("Buffer 3");
buf1.swap(buf3); // 交换 buf1 和 buf3 的所有权
buf1->print(); // 输出 "Message: Buffer 3"
buf3->print(); // 输出 "Message: New Buffer"
// 最后,buf1、buf2、buf3 超出作用域时,自动销毁相应的对象
return 0;
}
- shared_ptr不能通过“直接将原始这种赋值”来初始化
- std::shared_ptr 管理动态数组时,需要指定自定义删除器,因为 shared_ptr 的默认删除器(即 delete)不能直接用于删除数组。对于数组类型的对象,我们必须使用 delete[] 来正确地释放内存。
std::shared_ptr<int[]> p3(new int[10], [](int* p) { delete[] p; });
/*
std::shared_ptr<int[]> p3:shared_ptr 类型为 int[],即管理一个动态数组。
new int[10]:创建一个包含 10 个 int 元素的动态数组,内存分配在堆上。
[](int* p) { delete[] p; }:这是一个 lambda 表达式,定义了一个自定义删除器,负责调用 delete[] 来释放数组内存
*/
int main() {
int* p = new int(10);
std::shared_ptr ptr1(p); // ptr1 使用原始指针 p 初始化
std::shared_ptr ptr2(p); // 错误:ptr2 也使用原始指针 p 初始化,导致 double delete
return 0;
}
在此示例中,ptr1 和 ptr2 都管理同一个原始指针 p,这将导致 delete 被调用两次,从而引发未定义行为。
正确的做法:
始终使用 std::make_shared 或一个新的指针来创建 shared_ptr,以确保引用计数管理是安全的。
```cpp
#include <memory>
int main() {
auto ptr1 = std::make_shared<int>(10); // 使用 make_shared 正确创建 shared_ptr
auto ptr2 = ptr1; // ptr2 和 ptr1 共享所有权,引用计数增加
return 0;
}
通过 std::make_shared 创建 shared_ptr 可以避免使用原始指针初始化多个 shared_ptr。
解释:
在函数实参中创建 shared_ptr 可能会导致内存管理问题,尤其是在函数调用期间,如果不恰当使用,可能导致提前销毁对象或意外共享所有权。此外,使用 shared_ptr 作为函数参数时,通常需要额外的注意来确保内存正确管理。
错误示例:
```cppvoid foo(std::shared_ptr ptr) {
// 这里,ptr 是一个局部的 shared_ptr,当函数结束时 ptr 会被销毁
std::cout << *ptr << std::endl; // 正常使用,但是内存会在函数结束时释放
}
int main() {
foo(std::make_shared(10)); // 不推荐这样在函数实参中创建 shared_ptr
return 0;
}
在此示例中,std::make_shared<int>(10) 会在函数调用时创建一个新的 shared_ptr,该指针会在函数结束时销毁,这可能导致不必要的拷贝和对象生命周期问题。
正确的做法:
使用 std::shared_ptr 作为函数参数时,可以传递已有的 shared_ptr,避免在函数内部分配新的对象。
```cpp
#include <memory>
void foo(std::shared_ptr<int> ptr) {
std::cout << *ptr << std::endl;
}
int main() {
auto ptr = std::make_shared<int>(10); // 在外部创建 shared_ptr
foo(ptr); // 传递已有的 shared_ptr
return 0;
}
通过传递已创建的 shared_ptr 来避免在函数参数中直接创建新的 shared_ptr。
3. 通过 shared_from_this() 返回 this 指针
解释:
shared_from_this() 是 std::enable_shared_from_this 类模板提供的成员函数,允许一个类的实例(如果它是由 shared_ptr 管理的)返回一个指向该对象的 shared_ptr。但要确保在调用 shared_from_this() 时,该对象必须由 shared_ptr 管理,否则会抛出异常。
错误示例:
#include <iostream>
#include <memory>
class MyClass : public std::enable_shared_from_this<MyClass> {
public:
void foo() {
std::shared_ptr<MyClass> ptr = shared_from_this(); // 错误:如果不是由 shared_ptr 管理,抛出异常
std::cout << "Inside foo" << std::endl;
}
};
int main() {
MyClass* rawPtr = new MyClass();
rawPtr->foo(); // 错误:rawPtr 不是由 shared_ptr 管理,无法调用 shared_from_this()
return 0;
}
在此示例中,rawPtr 是一个普通的原始指针,而 shared_from_this() 只能在对象由 shared_ptr 管理时才有效。如果直接使用原始指针,调用 shared_from_this() 将导致程序崩溃。
正确的做法:
确保对象是通过 shared_ptr 管理的,然后再调用 shared_from_this()。
#include <iostream>
#include <memory>
class MyClass : public std::enable_shared_from_this<MyClass> {
public:
void foo() {
std::shared_ptr<MyClass> ptr = shared_from_this(); // 正确:shared_ptr 管理此对象
std::cout << "Inside foo" << std::endl;
}
};
int main() {
std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>(); // 使用 shared_ptr 管理对象
ptr->foo(); // 正确:对象由 shared_ptr 管理,可以调用 shared_from_this()
return 0;
}
通过 std::make_shared 管理对象,调用 shared_from_this() 就是安全的。
class A;
class B {
public:
std::shared_ptr a_ptr; ~B() { std::cout << "B destroyed" << std::endl; }};
int main() { std::shared_ptr a = std::make_shared(); std::shared_ptr b = std::make_shared();
a->b_ptr = b; // A 指向 B b->a_ptr = a; // B 指向 A,形成循环引用 // 当 a 和 b 超出作用域时,它们不会被销毁,因为有循环引用 return 0; } 在这个示例中,a 和 b 相互持有对方的 shared_ptr,导致它们的引用计数永远不会为 0,导致内存泄漏。 正确的做法: 使用 std::weak_ptr 来打破循环引用。weak_ptr 不会增加引用计数,因此它不会导致循环引用问题。 ```cpp #include #include class A; class B { public: std::weak_ptr a_ptr; // 使用 weak_ptr 代替 shared_ptr,避免循环引用 ~B() { std::cout << "B destroyed" << std::endl; } }; class A { public: std::shared_ptr b_ptr; ~A() { std::cout << "A destroyed" << std::endl; } }; int main() { std::shared_ptr a = std::make_shared(); std::shared_ptr b = std::make_shared(); a->b_ptr = b; // A 指向 B b->a_ptr = a; // B 通过 weak_ptr 指向 A,避免循环引用 // 当 a 和 b 超出作用域时,它们会被正确销毁 return 0; } 在这里,std::weak_ptr 代替了 shared_ptr,解决了循环引用的问题,避免了内存泄漏。std::weak_ptr 不增加引用计数:weak_ptr 不会增加对象的引用计数,且 weak_ptr 无法直接访问管理的对象,它的主要作用是作为观察者。 避免循环引用:当 shared_ptr 之间形成循环引用时,weak_ptr 可以用来打破这种循环,因为它不会影响引用计数。 过期检查:weak_ptr 可以通过 lock() 方法获取一个 shared_ptr,该方法会检查对象是否已被销毁。如果对象已被销毁,lock() 会返回一个空的 shared_ptr。 4. unique_ptr unique_ptr 不能被赋值给另一个 unique_ptr解释:std::unique_ptr 是 独占型 智能指针,它不支持复制操作。也就是说,不能将一个 unique_ptr 直接赋值给另一个 unique_ptr,因为这样会导致两个 unique_ptr 同时拥有对同一资源的控制,违背了 独占所有权 的原则。unique_ptr 只能通过 移动语义(std::move)来转移所有权。错误示例:```cpp#include #include int main() { std::unique_ptr ptr1 = std::make_unique(10); std::unique_ptr ptr2 = ptr1; // 错误:无法将 unique_ptr 复制到另一个 unique_ptr return 0; } 编译器会报错,提示无法复制 unique_ptr。 正确做法:使用 std::move 转移所有权 ```cpp #include #include int main() { std::unique_ptr ptr1 = std::make_unique(10); std::unique_ptr ptr2 = std::move(ptr1); // 正确:通过 std::move 转移所有权 if (!ptr1) { std::cout << "ptr1 is now null" << std::endl; } std::cout << *ptr2 << std::endl; // 输出 10 return 0; } 在这里,std::move 将 ptr1 的所有权转移给了 ptr2,之后 ptr1 变为 nullptr,但 ptr2 继续管理该对象。 unique_ptr 可以指向一个数组解释:std::unique_ptr 不仅可以管理单个对象,还可以管理动态数组。与管理单个对象类似,unique_ptr 会在其析构时自动调用 delete[] 来释放数组内存。如果你使用 unique_ptr 管理动态数组,必须使用 unique_ptr 来正确管理内存。 示例: #include #include int main() { std::unique_ptr arr = std::make_unique(5); // 使用 unique_ptr 管理数组 // 初始化数组元素 for (int i = 0; i < 5; ++i) { arr[i] = i * 10; } // 输出数组元素 for (int i = 0; i < 5; ++i) { std::cout << arr[i] << " "; } std::cout << std::endl; // 当 arr 超出作用域时,内存会自动释放 return 0; } 在这个例子中,arr 是一个 unique_ptr,它管理一个包含 5 个 int 元素的动态数组。当 arr 超出作用域时,它会自动释放内存。 注意:当 unique_ptr 管理数组时,必须使用 std::make_unique() 来创建数组,不能使用 std::make_unique(),因为后者是用于单个对象的。 unique_ptr 需要指定删除器的类型解释:std::unique_ptr 默认使用 delete 作为删除器来销毁对象。如果你使用 unique_ptr 管理一个数组,默认的删除器 delete 会导致错误,因为数组必须使用 delete[] 来删除。如果你需要处理其他类型的资源或自定义删除逻辑,可以指定一个 自定义删除器。 示例:默认删除器: #include #include int main() { std::unique_ptr ptr = std::make_unique(10); // 使用默认删除器 std::cout << *ptr << std::endl; // 输出 10 return 0; } 在这个例子中,ptr 使用默认的 delete 删除器来销毁对象。 自定义删除器:如果你需要在释放资源时执行其他操作(例如,记录日志、释放特定类型的资源等),你可以使用自定义删除器。自定义删除器通常是一个函数指针、函数对象或 lambda 表达式。 #include #include struct MyDeleter { void operator()(int* ptr) const { std::cout << "Custom delete for " << *ptr << std::endl; delete ptr; // 自定义删除操作 } }; int main() { // 使用自定义删除器 std::unique_ptr ptr(new int(10), MyDeleter()); std::cout << *ptr << std::endl; // 输出 10 return 0; } 在这个例子中,我们定义了一个 MyDeleter 结构体,它提供了一个自定义的删除操作。unique_ptr 在析构时会调用这个删除器。 自定义删除器用于数组: #include #include int main() { // 使用自定义删除器管理数组 std::unique_ptr> arr( new int[5], [](int* p) { std::cout << "Custom delete for array\n"; delete[] p; }); // 初始化数组元素 for (int i = 0; i < 5; ++i) { arr[i] = i * 10; } // 输出数组元素 for (int i = 0; i < 5; ++i) { std::cout << arr[i] << " "; } std::cout << std::endl; // 自动调用自定义删除器释放内存 return 0; } 在这个示例中,我们使用了 std::function 类型的自定义删除器来管理数组的释放。该删除器会在 unique_ptr 析构时调用,正确地释放数组内存。 5. weak_ptr弱引用的智能指针 share_ptr虽然已经很好用了,但是有一点share_ptr智能指针还是有内存泄露的情况,当两个对象相互使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptr, weak_ptr只是提供了对管理对象的一个访问手段。1. 基本用法 use_count()作用:返回与 weak_ptr 关联的 shared_ptr 的引用计数。 示例: #include #include int main() { std::shared_ptr sp = std::make_shared(10); // 创建 shared_ptr std::weak_ptr wp = sp; // 关联 weak_ptr std::cout << "use_count: " << wp.use_count() << std::endl; // 输出 1 { std::shared_ptr sp2 = wp.lock(); // 提升 weak_ptr 到 shared_ptr std::cout << "use_count after lock: " << wp.use_count() << std::endl; // 输出 2 } std::cout << "use_count after sp2 out of scope: " << wp.use_count() << std::endl; // 输出 1 return 0; } 解释:wp.use_count() 返回当前与 weak_ptr 关联的 shared_ptr 的数量。在 sp2 提升时,shared_ptr 的引用计数增加到 2;sp2 离开作用域后,引用计数降回 1。2. expired()作用:判断 weak_ptr 关联的对象是否已经被释放。如果对象已释放,则返回 true。 示例: #include #include int main() { std::weak_ptr wp; { std::shared_ptr sp = std::make_shared(10); // 创建 shared_ptr wp = sp; // 关联 weak_ptr std::cout << "Is expired? " << std::boolalpha << wp.expired() << std::endl; // 输出 false } // sp 超出作用域,对象被释放 std::cout << "Is expired? " << std::boolalpha << wp.expired() << std::endl; // 输出 true return 0; } 解释:expired() 返回 true,表示 weak_ptr 所关联的 shared_ptr 已经释放,对象不再存在。3. lock()作用:尝试从 weak_ptr 获取一个 shared_ptr。如果对象仍然存在,lock() 返回一个指向该对象的 shared_ptr;否则返回一个空的 shared_ptr。 示例: #include #include int main() { std::weak_ptr wp; { std::shared_ptr sp = std::make_shared(20); // 创建 shared_ptr wp = sp; // 关联 weak_ptr // 使用 lock 提升 weak_ptr if (auto sp2 = wp.lock()) { std::cout << "Object is alive, value: " << *sp2 << std::endl; // 输出 20 } else { std::cout << "Object is expired" << std::endl; } } // sp 超出作用域,对象被释放 if (auto sp2 = wp.lock()) { std::cout << "Object is alive, value: " << *sp2 << std::endl; } else { std::cout << "Object is expired" << std::endl; // 输出 "Object is expired" } return 0; } 解释:lock() 返回一个有效的 shared_ptr,前提是对象仍然存在。当 shared_ptr 管理的对象被销毁后,lock() 返回一个空的 shared_ptr。 2.weak_ptr返回this指针 weak_ptr 返回 this 指针与 shared_ptr 的区别在 C++ 中,weak_ptr 和 shared_ptr 都是智能指针,用于管理动态分配的内存。它们的使用场景和特性有所不同,尤其是在类成员函数中,涉及到 this 指针时,二者的行为和用途也有所区别。 shared_ptr 和 weak_ptr 的区别shared_ptr:shared_ptr 是 共享所有权 的智能指针,多个 shared_ptr 可以指向同一个对象,并且引用计数会增加,直到最后一个 shared_ptr 被销毁时,才会释放对象的内存。当你返回一个 shared_ptr 时,它会增加对象的引用计数,确保对象在引用存在时不会被销毁。weak_ptr:weak_ptr 是 弱引用,它不会增加引用计数,只是观察 shared_ptr 所管理的对象。weak_ptr 不会阻止对象的销毁,因此可以避免循环引用问题。当你返回 weak_ptr 时,不会影响对象的生命周期。如果需要访问对象,需要使用 lock() 方法提升为 shared_ptr。 为什么不能直接返回 this 指针作为 shared_ptr 或 weak_ptr返回 shared_ptr 时的危险: 直接返回 shared_ptr(this),即返回当前对象的 shared_ptr,会 增加引用计数,可能导致 循环引用 或 对象生命周期延长,从而导致内存泄漏。 返回 weak_ptr 的优势: 使用 weak_ptr 返回 this 指针时,不会改变引用计数,也不会导致循环引用,因此可以避免对象被意外地保留在内存中。可以在需要时通过 lock() 获取一个有效的 shared_ptr,如果对象已经被销毁,则返回空的 shared_ptr。 返回 this 指针的正确做法:使用 shared_from_this() 和 weak_ptr如果你想在类成员函数中返回当前对象的智能指针,通常应该使用 std::enable_shared_from_this 来实现。std::enable_shared_from_this 是一个模板类,它使得对象能够返回一个 shared_ptr 指向自身。 shared_from_this():std::enable_shared_from_this 允许对象在没有直接 shared_ptr 的情况下,安全地返回指向自身的 shared_ptr。只有通过 shared_ptr 创建的对象才能调用 shared_from_this(),因此在对象创建时必须使用 shared_ptr 来管理对象的生命周期。示例:使用 shared_from_this() 和 weak_ptr #include #include class MyClass : public std::enable_shared_from_this { public: MyClass() { std::cout << "MyClass constructor\n"; } ~MyClass() { std::cout << "MyClass destructor\n"; } // 返回 shared_ptr 指向当前对象 std::shared_ptr getSharedPtr() { // 返回一个 shared_ptr,该 shared_ptr 与当前对象共享所有权 return shared_from_this(); } // 返回 weak_ptr 指向当前对象 std::weak_ptr getWeakPtr() { return shared_from_this(); } void showMessage() { std::cout << "Hello from MyClass!" << std::endl; } }; int main() { // 创建一个 shared_ptr 管理 MyClass 对象 std::shared_ptr sp1 = std::make_shared(); // 获取 shared_ptr 和 weak_ptr std::shared_ptr sp2 = sp1->getSharedPtr(); // 返回 shared_ptr std::weak_ptr wp = sp1->getWeakPtr(); // 返回 weak_ptr sp2->showMessage(); // 调用对象的成员函数 std::cout << "use_count of sp1: " << sp1.use_count() << std::endl; // 输出引用计数,应该是 2 std::cout << "use_count of wp: " << wp.use_count() << std::endl; // weak_ptr 不增加引用计数,输出 2 // 清除 shared_ptr sp1.reset(); std::cout << "use_count after sp1.reset(): " << sp2.use_count() << std::endl; // 输出 1,sp2 仍然有效 // weak_ptr 尝试提升为 shared_ptr if (auto sp3 = wp.lock()) { sp3->showMessage(); // 如果对象仍然存在,输出 "Hello from MyClass!" } else { std::cout << "Object has been deleted" << std::endl; // 如果对象已销毁,输出该消息 } return 0; } 3. weak_ptr解决循环引用问题 假设有两个类 A 和 B,它们互相持有对方的 shared_ptr,这就是典型的循环引用场景。```cpp#include #include class B; // 前向声明 class A {public: std::shared_ptr b_ptr; // A 拥有 B 的 shared_ptr A() { std::cout << "A constructed\n"; } ~A() { std::cout << "A destroyed\n"; } };class B {public: std::shared_ptr a_ptr; // B 拥有 A 的 shared_ptr
B() { std::cout << "B constructed\n"; } ~B() { std::cout << "B destroyed\n"; } };int main() { // 创建 A 和 B 的 shared_ptr std::shared_ptr a = std::make_shared(); std::shared_ptr b = std::make_shared();
// 创建循环引用 a->b_ptr = b; b->a_ptr = a; // 对象 a 和 b 应该会被销毁,但由于循环引用,它们永远不会被销毁 return 0; } 为了解决循环引用的问题,我们可以将其中一个 shared_ptr 替换为 weak_ptr。这样,当其中一个对象被销毁时,weak_ptr 不会阻止另一个对象的销毁。 2. 解决循环引用的正确做法 ```cpp #include #include class B; // 前向声明 class A { public: std::shared_ptr b_ptr; // A 拥有 B 的 shared_ptr A() { std::cout << "A constructed\n"; } ~A() { std::cout << "A destroyed\n"; } }; class B { public: std::weak_ptr a_ptr; // B 拥有 A 的 weak_ptr,打破循环引用 B() { std::cout << "B constructed\n"; } ~B() { std::cout << "B destroyed\n"; } }; int main() { // 创建 A 和 B 的 shared_ptr std::shared_ptr a = std::make_shared(); std::shared_ptr b = std::make_shared(); // 创建非循环引用 a->b_ptr = b; b->a_ptr = a; // 这里使用 weak_ptr 而不是 shared_ptr // 对象 a 和 b 在 main 函数结束时会被正常销毁 return 0; } 解决方法解释:int main() {
int x = 10, y = 20;
// 使用表达式捕获
auto lambda = [sum = x + y]() {
std::cout << "Captured sum: " << sum << std::endl;
};
lambda(); // 输出:Captured sum: 30
return 0;
}
6. 泛型 Lambda
C++14 引入了 泛型 Lambda,允许使用 自动类型推导(auto) 来定义参数类型,从而使 Lambda 可以处理不同类型的参数。
[capture](auto parameter1, auto parameter2) { function_body; }
```cpp
#include <iostream>
int main() {
auto generic_lambda = [](auto a, auto b) {
return a + b;
};
std::cout << "Integers: " << generic_lambda(3, 5) << std::endl; // 输出:8
std::cout << "Doubles: " << generic_lambda(3.5, 2.5) << std::endl; // 输出:6
std::cout << "Strings: " << generic_lambda(std::string("Hello"), std::string(" World")) << std::endl;
// 输出:Hello World
return 0;
}
capture mutable { function_body; }
#include <iostream>
int main() {
int x = 10;
auto mutable_lambda = [x]() mutable {
x += 5;
std::cout << "Inside lambda: " << x << std::endl;
};
mutable_lambda(); // 输出:Inside lambda: 15
std::cout << "Outside lambda: " << x << std::endl; // 输出:Outside lambda: 10
return 0;
}
C++标准库(STL,Standard Template Library)是一个强大的模板库,提供了一些高效的数据结构和算法。STL包含三大主要组件:容器(Containers)、迭代器(Iterators)和算法(Algorithms)。
容器是用于存储和管理数据的类模板。STL提供了多种容器,主要分为顺序容器和关联容器。
顺序容器:数据按插入顺序存储。
vector:动态数组,支持随机访问。
deque:双端队列,支持从两端插入和删除。
list:双向链表,支持从两端插入和删除,但不支持随机访问。
array:固定大小的数组,性能优于vector,但大小固定。
关联容器:数据通过键值对(key-value)存储,常用于查找、插入、删除操作。
set:存储唯一的元素,按顺序排列。
map:存储键值对,按键排序。
unordered_set:存储唯一的元素,元素无序,基于哈希表。
unordered_map:存储键值对,元素无序,基于哈希表。
容器适配器:
stack:栈,遵循先进后出(LIFO)规则。
queue:队列,遵循先进先出(FIFO)规则。
priority_queue:优先队列,元素按优先级顺序排列。
迭代器是用于遍历容器元素的对象,类似于指针。迭代器支持对容器元素进行读取和修改。
常用迭代器类型:
begin():返回指向容器第一个元素的迭代器。
end():返回指向容器最后一个元素之后位置的迭代器。
rbegin():返回指向容器最后一个元素的逆向迭代器。
rend():返回指向容器第一个元素之前位置的逆向迭代器。
2. 迭代器操作:
*it:解引用,访问迭代器指向的元素。
it++:将迭代器移动到下一个元素。
it--:将迭代器移动到前一个元素。
STL提供了大量的标准算法,用于对容器中的数据进行排序、查找、修改等操作。常用的算法包括:
sort():对容器中的元素进行排序。
stable_sort():稳定排序,保持相等元素的相对顺序。
reverse():反转容器中的元素顺序。
2. 查找:
find():查找指定元素,返回指向元素的迭代器。
binary_search():二分查找,返回布尔值,指示是否找到目标元素。
3. 修改:
fill():将容器中的所有元素设置为指定的值。
copy():将一个容器的元素复制到另一个容器中。
4. 集合操作:
merge():将两个有序的集合合并为一个。
set_union():计算两个集合的并集。
set_intersection():计算两个集合的交集。
5. 其他:
accumulate():对容器中的元素执行累加操作。
for_each():对容器中的每个元素执行给定的操作。
提供了强大的文本处理能力。通过 std::regex 和相关函数,可以高效地进行文本的匹配、替换、分割等操作,极大提高了代码的灵活性和可维护性。掌握正则表达式在 C++ 中的使用,能帮助解决许多实际的字符串处理问题。
C++ 的 头文件定义了几个关键的组件:
std::regex:用于存储正则表达式的对象。
std::smatch 和 std::cmatch:分别用于存储 std::string 和 const char* 类型的匹配结果。
std::regex_match:用于检查整个字符串是否与正则表达式完全匹配。
std::regex_search:用于查找字符串中是否存在与正则表达式匹配的部分。
std::regex_replace:用于替换匹配的字符串。
int main() {
// 创建一个正则表达式对象,匹配数字
std::regex pattern(R"(\d+)"); // R"()" 语法用于避免反斜杠转义
return 0;
}
2. std::regex_match - 检查完整匹配
std::regex_match 用于检查整个字符串是否完全匹配正则表达式。
```cpp
#include <iostream>
#include <regex>
int main() {
std::string text = "12345";
std::regex pattern(R"(\d+)"); // 匹配一个或多个数字
if (std::regex_match(text, pattern)) {
std::cout << "Match!" << std::endl;
} else {
std::cout << "No match." << std::endl;
}
return 0;
}
#include <iostream>
#include <regex>
int main() {
std::string text = "There are 123 apples";
std::regex pattern(R"(\d+)"); // 匹配一个或多个数字
if (std::regex_search(text, pattern)) {
std::cout << "Found a match!" << std::endl;
} else {
std::cout << "No match." << std::endl;
}
return 0;
}
int main() {
std::string text = "I have 123 apples and 456 oranges";
std::regex pattern(R"(\d+)"); // 匹配数字
std::string result = std::regex_replace(text, pattern, "X");
std::cout << result << std::endl; // 替换数字为 X
return 0;
}
2. std::smatch - 获取匹配结果
std::smatch 用于存储和提取正则表达式的匹配结果。
```cpp
#include <iostream>
#include <regex>
int main() {
std::string text = "My phone number is 123-456-7890";
std::regex pattern(R"(\d{3}-\d{3}-\d{4})"); // 匹配电话号码格式
std::smatch match; // 存储匹配结果
if (std::regex_search(text, match, pattern)) {
std::cout << "Found phone number: " << match.str(0) << std::endl;
}
return 0;
}
int main() {
std::string text = "My email is example@example.com";
std::regex pattern(R"((\w+)@(\w+.\w+))"); // 捕获用户名和域名
std::smatch match;
if (std::regex_search(text, match, pattern)) {
std::cout << "Username: " << match[1] << std::endl;
std::cout << "Domain: " << match[2] << std::endl;
}
return 0;
}