当前位置: 首页 > news >正文

网站描述优化秦皇岛做网站公司排名

网站描述优化,秦皇岛做网站公司排名,网站seo推广方案,国通快速免费建站C中为什么要引入make_shared,它有什么优点 1. 减少内存分配次数 使用 make_shared 时#xff0c;内存分配只发生一次#xff0c;它同时分配了对象和控制块#xff08;用于管理引用计数等信息#xff09;。而如果直接使用 new 创建对象并传递给 shared_ptr#xff0c;则会…C中为什么要引入make_shared,它有什么优点 1. 减少内存分配次数 使用 make_shared 时内存分配只发生一次它同时分配了对象和控制块用于管理引用计数等信息。而如果直接使用 new 创建对象并传递给 shared_ptr则会分别进行两次内存分配一次为对象本身另一次为控制块。 // 使用 new std::shared_ptrMyClass ptr1(new MyClass());// 使用 make_shared std::shared_ptrMyClass ptr2 std::make_sharedMyClass();在上面的例子中ptr1 进行了一次对象的内存分配和一次控制块的内存分配而 ptr2 只进行了一次内存分配。 2. 提高性能 由于 make_shared 只进行一次内存分配它可以减少内存分配和释放的开销尤其是在频繁创建和销毁对象的场景下。这样可以提高程序的性能。 3. 避免内存泄漏 使用 make_shared 可以减少内存泄漏的风险。因为它提供了一种更安全的方式来创建 shared_ptr避免了在使用 new 时可能忘记释放内存的情况。使用 make_shared即使在构造过程中发生异常内存也会被正确管理。 4.简化代码 使用make_shared创建代码可以简化创建shared_ptr实例的代码让代码逻辑更加清晰 shared_ptr相关的知识点 shared_ptr基础与之对应的是unique_ptr但使用它有限制比如不能共享所有权。而std::shared_ptr是一个可以共享控制权的智能指针可以自动管理动态分配的对象生命周期。 new和shared_ptr在没有make_shared之前我们通常这样创建shared_ptrstd::shared_ptrint sp(new int(5));。这个过程其实做了两个动作创建一个临时对象又创建一个shared_ptr对象。如果第一步的内存分配成功但第二步抛出异常那么就会发生内存泄漏。 make_shared内部原理make_shared将对象的动态内存和控制块内存存储引用计数的那块内存一次性分配减少了内存分配的次数。 例如auto sp std::make_sharedint(5);这种方式比前一种方式高效并且更加安全。 性能更好单次内存分配意味着分配器只调用一次这比多次调用可能导致的内存碎片问题更加高效。此外这种方式在多线程环境中也有一定优势减少了分配内存时的竞争。 异常安全使用make_shared如果在创建过程中抛出异常因为它是“全有或全无”的过程所以不需要担心部分资源分配成功导致的内存泄漏。例如make_shared可以保证在对象和控制块都构建成功之后才开始使用它们。 C的string内部使用的是堆内存还是栈内存 这个题目主要考察的就是string内部的一个优化首先std::string这个对象本身是保存在栈内存的单这个对象所管理的数据是保存在堆中的但是在string中存在一个短字符串优化SSO这个优化让字符串比较小时字符串个数少于一个值时会将这个数据对象也存在在string对象的缓冲区中此时这个对象管理的数据就在栈中。而当字符串的长度超过了这个值时会将管理的数据放入到堆的空间中。此时这个对象管理的数据就在栈中。  说明C中的futurepromisepackaged_task,async的区别 1. std::future 定义: std::future 是一个用于获取异步操作结果的对象。它可以从一个异步任务中获取结果并且可以在结果可用时进行等待。用途: 用于获取异步操作的结果通常与 std::promise、std::packaged_task 或 std::async 一起使用。特性: 可以通过 get() 方法获取结果如果结果尚不可用它会阻塞当前线程直到结果可用。可以检查结果是否已经准备好通过 valid() 和 wait_for() 等方法。 2. std::promise 定义: std::promise 是一个用于设置异步操作结果的对象。它可以与 std::future 结合使用将结果从一个线程传递到另一个线程。用途: 用于在一个线程中设置值另一个线程可以通过 std::future 获取这个值。特性: 通过 set_value() 方法设置结果或者通过 set_exception() 方法设置异常。创建与 promise 关联的 future 对象使用 get_future() 方法。 3. std::packaged_task 定义: std::packaged_task 是一个可调用对象它可以封装任何可调用的任务如函数、lambda 表达式等并将其与 std::future 关联。用途: 用于将任务与一个 future 关联起来使得可以异步执行这个任务并获取结果。特性: 可以通过 operator() 调用封装的任务。通过 get_future() 方法获取与 packaged_task 关联的 future 对象。适合于需要将任务封装并在不同线程中执行的场景。 4. std::async 定义: std::async 是一个用于异步执行函数的标准库函数。它返回一个 std::future可以用来获取异步操作的结果。用途: 用于简单地启动异步任务并获取结果通常不需要手动管理线程。特性: 可以指定任务的执行策略如 std::launch::async 或 std::launch::deferred。自动管理线程用户不需要显式地创建和管理线程。std::launch::async: 任务立即在新线程中执行适合需要并发执行的场景。std::launch::deferred: 任务延迟到调用 get() 时执行适合轻量任务或可能不会执行的情况。 总结 组件定义用途主要特性std::future获取异步操作结果的对象获取异步操作的结果阻塞等待结果检查结果状态std::promise设置异步操作结果的对象在线程中设置值另一个线程获取设置值或异常创建与 future 关联的对象std::packaged_task可调用对象封装任务与 future 关联封装任务并异步执行获取结果封装任意可调用对象调用后可以获取结果std::async异步执行函数的标准库函数简单启动异步任务并获取结果自动管理线程支持执行策略 简化之后的说明如下 std::future用于在不同线程间传递结果。它可以从异步操作中获取返回值。 std::promise用于设置一个值或者异常这些值或异常会被对应的 std::future 获取。 std::packaged_task包装一个可调用对象函数、lambda表达式、bind表达式等方便异步执行并将结果通过 std::future 获取。 std::async用于异步地启动任务并返回一个 std::future 对象以便获取任务结果。 这些特性在不同场合下分别发挥各自的作用 std::future 和 std::promise 更适用于实现自定义的异步操作。std::packaged_task 更适合将现有的函数包装为异步任务而无需显式使用线程。std::async 最为简便用于从简化的异步调用中获取结果。 C的async在使用的时候有什么注意事项  选择合适的启动策略launch policystd::async 可以接受一个启动策略作为参数如 std::launch::async 和 std::launch::deferred。std::launch::async 会创建一个新的线程而 std::launch::deferred 只会在需要结果时才开始任务。所以要根据实际需求选择合适的策略。注意一定要明确指定创建策略。如果不明确指定创建策略以上两个都不是 async 的默认策略而是 undefined它是一个基于任务的程序设计内部有一个调度器线程池会根据实际情况决定采用哪种策略。 保证获取结果future.getstd::async 返回一个 std::future 对象一定要记得调用 future.get() 或者 future.wait() 来获取任务结果或者等待任务完成。如果没有获取结果程序的行为是不确定的。 异常处理当异步任务中抛出异常时这些异常会被 future.get() 捕获。所以一定要在调用 future.get() 时准备好捕获和处理可能的异常。 确认 async 对象的生命周期如果从 std::async 获得的 std::future 未被移动或绑定到引用则在完整表达式结尾这个std::future对象会被销毁。 扩展知识 返回值的类型推导std::async 会自动推导返回值类型而且会在异步任务完成后把结果存储在 std::future 对象里。这种类型推导可以让代码更加简洁不需要明确指定返回值类型。 任务的生命周期异步任务的生命周期与 std::future 对象绑定。如果 future 对象被销毁那么异步任务也会被取消。所以确保 future 对象的生命周期覆盖任务的执行时间。 共享结果std::shared_future有时候你可能需要多个线程访问同一个异步结果这时可以使用 std::shared_future。它可以让多个线程共享异步结果而不是使用单一的 std::future确保多线程访问时的安全性。 并发虽然 std::async 提供了一种简单实用的并发机制但在实际应用中你可能还需要使用其他并发容器如 std::mutex、std::lock_guard、std::atomic 等来处理复杂的共享数据访问问题。 二叉搜索树的删除逻辑  在二叉搜索树Binary Search Tree, BST中删除节点的逻辑相对复杂因为我们需要保持树的性质。删除节点的过程可以分为三种主要情况 删除的节点是叶子节点没有子节点删除的节点有一个子节点删除的节点有两个子节点 删除逻辑 1. 删除叶子节点 直接将该节点从树中移除。 2. 删除有一个子节点的节点 将该节点的父节点指向该节点的唯一子节点从而移除该节点。 3. 删除有两个子节点的节点 此时我们需要找到该节点的后继节点在右子树中最小的节点或前驱节点在左子树中最大的节点然后用该节点的值替换要删除的节点的值并递归地删除该后继或前驱节点。 C 实现 以下是一个简单的 C 实现展示了二叉搜索树的删除逻辑 #include iostreamstruct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} };class BST { public:TreeNode* root;BST() : root(nullptr) {}// 插入节点TreeNode* insert(TreeNode* node, int val) {if (!node) return new TreeNode(val);if (val node-val)node-left insert(node-left, val);elsenode-right insert(node-right, val);return node;}// 删除节点TreeNode* deleteNode(TreeNode* root, int key) {if (!root) return nullptr;if (key root-val) {root-left deleteNode(root-left, key);} else if (key root-val) {root-right deleteNode(root-right, key);} else {// 找到要删除的节点if (!root-left) {return root-right; // 只有右子树} else if (!root-right) {return root-left; // 只有左子树} else {// 有两个子节点找后继节点TreeNode* successor minValueNode(root-right);root-val successor-val; // 替换值root-right deleteNode(root-right, successor-val); // 删除后继节点}}return root;}// 找到右子树中的最小节点TreeNode* minValueNode(TreeNode* node) {TreeNode* current node;while (current current-left) {current current-left;}return current;}// 中序遍历用于测试void inorder(TreeNode* node) {if (node) {inorder(node-left);std::cout node-val ;inorder(node-right);}} };int main() {BST tree;tree.root tree.insert(tree.root, 50);tree.insert(tree.root, 30);tree.insert(tree.root, 20);tree.insert(tree.root, 40);tree.insert(tree.root, 70);tree.insert(tree.root, 60);tree.insert(tree.root, 80);std::cout Inorder traversal of the BST: ;tree.inorder(tree.root);std::cout std::endl;std::cout Delete 20\n;tree.root tree.deleteNode(tree.root, 20);std::cout Inorder traversal after deleting 20: ;tree.inorder(tree.root);std::cout std::endl;std::cout Delete 30\n;tree.root tree.deleteNode(tree.root, 30);std::cout Inorder traversal after deleting 30: ;tree.inorder(tree.root);std::cout std::endl;std::cout Delete 50\n;tree.root tree.deleteNode(tree.root, 50);std::cout Inorder traversal after deleting 50: ;tree.inorder(tree.root);std::cout std::endl;return 0; }C什么场景下使用锁lock什么场景下使用原子变量atomic? 锁lock和原子变量atomic都可以用作同步机制它们有各自的适用场景 1) 使用锁的场景 当需要保护长时间访问的临界区时比如复杂的操作或逻辑如链表、树等复杂数据结构的操作。当多个共享资源需要同步访问时锁可以一次性锁定多个资源确保整体一致性。在涉及到复杂的操作时比如需要一次性更新多个共享变量。 2) 使用原子变量的场景 当操作可以在一个原子步骤内完成时比如简单的整数增减、标志位切换。当性能非常关键且锁的开销和上下文切换的成本过高时。原子操作通常比使用锁更轻量级。用于实现非阻塞算法时因为原子变量不会导致线程挂起而等待锁释放。 建议优先使用原子变量如果发现使用原子变量不能满足同步机制的需求那就使用锁。 使用锁来保护的例子 std::mutex mtx; std::mapint, std::string sharedMap;void insertIntoMap(int key, const std::string value) {std::lock_guardstd::mutex lock(mtx);sharedMap[key] value; }使用原子变量的例子 class AtomicCounter { public:AtomicCounter() : count(0) {}// 增加计数void increment() {count.fetch_add(1, std::memory_order_relaxed);}// 获取当前计数int get() const {return count.load(std::memory_order_relaxed);}private:std::atomicint count; // 原子变量 }; 扩展知识 1) 锁的类型 互斥锁Mutex最常见的普通锁用于保护一个共享资源。读写锁Read-Write Lock允许多个读者并行访问但写者访问需要独占。自旋锁Spinlock线程在等待时会不断轮询锁状态而不是挂起非常适合短时间持有锁的场景。 2) 原子操作 可以使用 std::atomic 库提供的原子类型如 std::atomicint, std::atomicbool, atomic 是个模板类你还可以使用 double 等类型填充。这些操作通常由硬件直接支持比如 x86 架构的 Lock 前缀指令确保读取-修改-写入一个不可分割的操作。 C中锁的底层原理是什么 1. 互斥锁Mutex 互斥锁是最常见的锁类型用于保护共享数据。C11 引入了 mutex 头文件提供了 std::mutex 类。 实现原理 原子操作互斥锁的实现依赖于原子操作例如测试并设置Test-and-Set或比较并交换Compare-and-SwapCAS。这些操作通常由 CPU 提供并且是线程安全的。系统调用在用户空间中互斥锁的状态锁定或未锁定通常用一个标志位表示。当一个线程尝试获取锁时它会检查这个标志位。如果锁未被占用线程可以成功获取锁并将标志位设置为已锁定。如果锁已被占用线程可能会进入阻塞状态直到锁被释放。调度操作系统负责调度和唤醒线程。当一个线程释放锁时操作系统会选择一个等待线程来获取锁。 2. 自旋锁Spinlock 自旋锁是一种轻量级锁适用于锁持有时间非常短的场景。 实现原理 自旋锁使用原子操作来检查锁的状态。如果锁未被占用线程会通过原子操作获取锁如果锁已被占用线程会不断循环自旋检查锁的状态直到锁可用。自旋锁避免了线程上下文切换的开销但在持锁时间较长时可能会导致 CPU 资源浪费。 3. 读写锁Read-Write Lock 读写锁允许多个线程同时读取共享资源但在写入时会阻塞其他线程。 实现原理 读写锁通常使用一个计数器来跟踪当前的读操作数量和一个互斥锁来保护写操作。当一个线程请求写锁时它会检查当前是否有读锁或写锁被持有。如果有写锁请求将被阻塞如果没有线程可以获得写锁。当一个线程请求读锁时它会检查当前是否有写锁被持有。如果没有线程可以获得读锁并增加读锁计数器。 4. 条件变量Condition Variable 条件变量用于线程间的同步允许一个或多个线程在某个条件发生时被唤醒。 实现原理 条件变量通常与互斥锁一起使用。线程在等待条件变量时会释放互斥锁并在条件满足时被唤醒。条件变量的实现通常依赖于操作系统提供的信号量或其他同步原语。 5. 内存屏障Memory Barrier 在多线程环境中内存屏障用于确保特定的内存操作顺序以避免编译器和 CPU 的优化导致的数据不一致。 实现原理 内存屏障可以防止编译器重排代码确保在屏障之前的所有操作在屏障之后的操作之前完成。在 C 中可以使用原子操作和内存序memory order来控制这些操作的顺序。
http://www.hkea.cn/news/14278167/

相关文章:

  • 多元网站wordpress 点赞 开启
  • 网站建设任务平台软件开发工资高吗
  • 网站会员功能介绍jquery+js网站模板免费下载
  • 还有哪些行业可以做垂直网站菲斯曼售后服务中心
  • 常德行业网站亦庄网站建设公司
  • 怎么制作网站视频教程步骤建筑行业官网
  • 可信的郑州网站建设邢台做移动网站费用
  • 东莞做网站优化哪家好网站开发用的开源系统
  • wordpress网站翻译文本文档做网站
  • 课程网站建设的财务分析上海第五届中国国际进口博览会地址
  • 用爬虫做网站软件开发步骤包括哪些过程
  • 怎么访问域名网站制作灯笼图片
  • 建设网站费用明细做我的奴隶腾讯网站
  • 医院手机网站购物网站首页分成几个模块
  • 成都网站建设推荐到访率公司网站链接的常见形式
  • 携程旅行的网站建设腾讯云网站免费建设
  • 贵阳网站建设zu97百度手机网站提交
  • 保健品网站模版网站与经营网站
  • 网站开发公司会计服务区里可以做多少个网站
  • 软件库网站大全网站设计制作公司排名
  • 口碑好的网站开发公司计算机网络技术就业方向及前景
  • 有没有正规的毕设代做网站健康养老网站建设
  • 建设银行哪个是假网站百度风云榜明星
  • 化妆品网站建设推广方案大连市营商环境建设局网站
  • 网站建设论文 优帮云企业年金规定
  • 北京网络公司的网站建设网站需要学习什么
  • 网站的建设合同是否交印花税网站质量度
  • 做兼职哪个网站好四川成都装修公司排名
  • 制作彩票网站需要多少钱去男科医院花了9000多
  • 珠海企业免费建站技术支持 昆明网站建设