C++编程中的智能指针现代C++内存管理利器

【Zemax光学设计实训一】---单透镜光学设计 建立一个焦距为100mm、入瞳直径为25mm的单透镜系统,该透镜中心厚度为4mm,其两个面的曲率半径分别为100mm、-100mm,全视场2w=10°。该系统在可见光下工作,采用BK7玻璃,光阑设置在人射光线遇到的透镜的第一个光学表面;并对其展开优化。 阅读详情

智能指针的起源与必要性

在传统的C++编程中,动态内存管理是一项繁琐且易错的任务。开发者需要使用`new`操作符手动分配内存,并在对象生命周期结束时使用`delete`操作符精确地释放内存。这种手动管理方式极易导致内存泄漏、悬空指针或重复释放等问题,尤其是在异常抛出或代码路径复杂的情况下。为了解决这些痛点,现代C++标准库引入了智能指针,它们通过RAII(资源获取即初始化)范式,将内存管理等资源的管理职责委托给对象本身,从而实现了自动化的、异常安全的内存管理。

RAII:智能指针的设计基石

智能指针的核心思想建立在RAII原则之上。RAII是C++中管理资源(如内存、文件句柄、网络连接等)的关键惯用法。其基本思想是:在对象的构造函数中获取资源,在析构函数中释放资源。由于C++语言保证了对象在离开其作用域时(无论是正常离开还是因异常离开),其析构函数都会被自动调用,因此资源也能被自动且正确地释放。智能指针正是将动态分配的内存作为一种资源,封装在一个类对象内部。当智能指针对象本身被销毁时,其析构函数会负责释放它所管理的内存,程序员无需手动干预。

自动化生命周期管理

通过将裸指针封装在智能指针对象内部,内存的生命周期便与智能指针对象的生命周期绑定。例如,一个在局部作用域内创建的`std::unique_ptr`,当程序执行流离开这个作用域时,`std::unique_ptr`的析构函数会被调用,进而自动`delete`其拥有的内存。这种机制极大地简化了内存管理,避免了因遗忘`delete`而造成的内存泄漏。

异常安全保证

在手动管理内存的代码中,如果`new`和`delete`之间发生了异常,且该异常未被局部捕获,则`delete`语句将不会被执行,导致内存泄漏。而使用智能指针,即使发生异常,栈展开过程也会销毁局部智能指针对象,触发其析构函数并释放内存,从而确保了异常安全。

现代C++中的主要智能指针类型

C++11标准引入了三种主要的智能指针,定义在``头文件中,它们各自针对不同的所有权模型设计。

std::unique_ptr:独占所有权的守护者

`std::unique_ptr`是一种独占所有权的智能指针。在任意时刻,只有一个`unique_ptr`实例可以拥有并管理一个给定的对象。它不可复制,但支持移动语义,这意味着所有权可以从一个`unique_ptr`转移至另一个。这种设计非常适用于需要明确、单一所有权语义的场景。当`unique_ptr`被销毁时,它会自动删除其指向的对象。此外,它还可以通过自定义删除器来管理非`new`分配的资源(如`malloc`分配的内存或文件句柄),提供了极大的灵活性。

std::shared_ptr:共享所有权的解决方案

`std::shared_ptr`实现了共享所有权模型。多个`shared_ptr`实例可以共同拥有同一个对象。它内部使用引用计数机制来跟踪有多少个`shared_ptr`指向同一对象。每当一个`shared_ptr`被复制时,引用计数增加;当它被销毁或重置时,引用计数减少。当引用计数降为零时,说明已经没有`shared_ptr`拥有该对象,其所管理的对象便会被自动销毁。`shared_ptr`适用于需要多个参与者共享访问同一对象,且无法明确由谁负责释放资源的场景。

std::weak_ptr:打破循环引用的助手

`std::weak_ptr`是一种不控制对象生命周期的智能指针,它是对`std::shared_ptr`所管理对象的一种弱引用。`weak_ptr`的构造和析构不会影响其指向对象的引用计数。它的主要作用是解决`shared_ptr`可能带来的循环引用问题。例如,两个对象各自持有一个指向对方的`shared_ptr`,会导致引用计数永远不为零,从而产生内存泄漏。如果将其中一个指针改为`weak_ptr`,即可打破循环。要访问`weak_ptr`所指向的对象,必须先将其转换为`shared_ptr`(例如使用`lock()`方法),如果对象还存在,则访问成功;如果对象已被销毁,则获得一个空的`shared_ptr`。这提供了安全地访问可能已失效对象的能力。

使用实践与最佳实践

在现代C++编程中,应优先使用智能指针来管理动态分配的资源,尽量避免直接使用裸指针和手动内存管理。通常,默认应首先考虑使用`std::unique_ptr`,因为它开销最小,语义最清晰。只有在确实需要共享所有权时,才使用`std::shared_ptr`。当存在循环引用风险或需要观察共享对象而不影响其生命周期时,配合使用`std::weak_ptr`。

避免混合使用裸指针与智能指针

一旦将原始指针交给智能指针管理,就不应再使用原始指针来访问或删除资源,否则会导致未定义行为。同时,应避免使用`get()`方法获取的裸指针去创建另一个独立的智能指针。

使用make_unique和make_shared

创建智能指针时,推荐使用`std::make_unique`(C++14)和`std::make_shared`函数模板,而不是直接使用`new`表达式。这样做的好处包括:代码更简洁、异常更安全(避免了因参数求值顺序可能导致的内存泄漏),并且对于`make_shared`而言,通常只需一次内存分配(将对象本身和引用计数控制块分配在连续的内存区域),可能带来性能提升和内存使用效率的优化。

总结

智能指针是现代C++高效、安全编程不可或缺的工具。它们通过自动化内存管理,显著降低了内存相关错误的概率,提高了代码的健壮性和可维护性。深入理解`unique_ptr`、`shared_ptr`和`weak_ptr`的不同所有权语义和适用场景,并遵循最佳实践,是每一位C++程序员迈向现代C++编程范式的重要一步。它们的存在使得开发者能够将精力更多地集中在业务逻辑实现上,而非底层资源管理的细节上。

DSI3协议介绍 分布式系统接口(DSI)是一种通用且功能强大的总线协议,旨在将多个遥感器和执行器设备互连到中央控制模块。该网络的主要目标应用是汽车安全系统。这种应用的一些特点是需要低成本、高鲁棒性、中速互连,仅限于两根导线。此外,它必须具有故障安全性、确定性和良好的EMC特性。即使具有所有级别的智能和可编程性的设备都可以连接到网络,远程设备也必须能够通过简单的状态机实现。由于模块尺寸非常重要,因此中央模块和远程单元中的最小组件至关重要。 阅读详情

相关推荐

简化神经元模型6 -- Hindmarsh-Rose Model

简化神经元模型 6 Hindmarsh-Rose Model

weixin_73557167的博客 1427

智能指针深度解析:C++内存管理的神奇利器

观察下列程序,正常情况下,程序new的对象我们能正常释放,但是当抛异常出现后,后⾯的delete没有得到执行,所以内存泄漏了,所以我们需要new以后捕获异常,捕获到异常后delete内存,再把异常抛出(将注释部分取消即可)。但是因为new本身也可能抛异常,两个new和Divide同时有问题呢,那我们就要套很多个try---catch语句,就很麻烦,因此C++引入了智能指针这位重量级人物。在C++编程中,是一个至关重要的问题,尤其是涉及动态内存分配、文件操作、网络连接和线程同步等场景时。

2302_79376097的博客 2030

给麒麟KylinOS服务器加把锁:手把手配置符合等保要求的密码强度策略

本文详细介绍了如何在麒麟KylinOS服务器上配置符合等保要求的密码强度策略,包括pwquality.conf和login.defs的核心参数设置、等保合规的进阶配置技巧以及企业环境中的平衡艺术。通过实战案例和测试方法,帮助管理员构建坚固的密码防线,确保系统安全。

weixin_33749242的博客 419

[C++中的智能指针现代C++内存管理的利器]

智能指针是现代C++内存管理的基石,它将开发者从繁琐且易错的手动内存管理中解放出来。通过`unique_ptr`、`shared_ptr`和`weak_ptr`的有机组合,开发者可以根据不同的所有权需求,构建出既安全又高效的C++应用程序。拥抱智能指针,意味着拥抱更现代的C++开发实践,是每一位C++开发者迈向编写高质量代码的必经之路。

WXFLMB的博客 927

基于Django+Vue3的目标检测系统设计与实现,Web前后端分离,Vue3前台系统+Django后台管理系统开发,YOLOv11 Web目标检测,实现图片、视频和摄像头检测等功能,全网独发

基于Django+Vue3的智能目标检测系统设计与实现,Web前后端分离,YOLOv11 Web目标检测,Vue3前台系统+Django后台管理系统开发,实现图片检测、视频检测、摄像头检测、登录、注册和个人中心功能

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 3667

IEC61850规约客户端软件开发实战(第二章)

本文介绍了基于IEC61850规约的客户端软件开发过程,重点阐述功能界面设计和服务端连接功能的实现。界面采用Qt框架设计,包含功能按钮区、数据模型展示区(使用QTreeWidget)和状态显示区。连接功能通过libiec61850开源库实现,详细说明了连接流程、句柄创建及状态管理的代码实现。测试结果表明系统能正确识别连接状态,并通过界面直观显示。文章为IEC61850规约软件开发提供了实用参考,下期将分享规约模型数据加载的实现过程。

半青年的博客 2855

C++中的智能指针现代C++内存管理的利器

C++智能指针是现代C++内存管理工具箱中的基石。通过将资源管理的责任从开发者转移给对象生命周期,它们极大地减少了内存相关的错误,使代码更加简洁、安全和易于维护。理解和熟练运用`std::unique_ptr`,`std::shared_ptr`和`std::weak_ptr`,是现代C++程序员必备的技能,也是编写高质量C++应用程序的关键一步。

pmrplc的博客 642

【AI】在RK3576上,使用RKNN实现MeloTTS(文本转语音)

在瑞芯微的RK3576上实现TTS(文本转语音)功能,使用CPU实现时,几乎把CPU资源全部耗尽,没有实用价值。通过将MeloTTS转为onnx,再转为RKNN,来使用RK3576的NPU硬件加速,成功实现TTS。

郭老二 563

【C++高阶】深入理解C++智能指针:掌握RAII与内存安全的利器

在C++的浩瀚宇宙中,内存管理一直是程序员们必须面对的重要课题。从早期的手动分配与释放,到现代C++标准库中引入的智能指针,每一次进步都标志着C++在提升开发效率、减少错误风险方面的巨大飞跃。智能指针,作为C++11及后续版本中不可或缺的一部分,不仅极大地简化了资源管理,还通过其独特的机制有效防止了内存泄漏和野指针的出现,成为了现代C++编程中不可或缺的工具

EterNity_TiMe_的计算机学习之路 1万+

第4-4课:状态压缩与动态规划

什么是状态压缩 动态规划问题中重要的一环就是确定状态的定义,在大多数情况下,状态中所包含的信息并不多,比如线性规划问题的状态一般表示为 d[i,j](i 表示状态的位置,j 表示状态的阶段),算法实现时可用二维数组保存计算过程中的状态。但是也有一些问题,它的状态中包含的信息很多,比如它的状态可能是一个集合中各个元素的情况,或者是像铺瓷砖问题这样,是某一行的覆盖状态。如果沿用简单的状态表示方法,则可...

oRbIt 的专栏 700

【从零到一】Jetson Nano上YOLOv5部署完全攻略:零基础小白也能玩转边缘AI推理

文章摘要: 本文详细介绍了在Jetson Nano边缘设备上部署YOLOv5目标检测模型的完整流程。从硬件准备、系统镜像烧录、基础环境配置,到CUDA/cuDNN验证、Python虚拟环境搭建及PyTorch安装,逐步指导开发者完成部署准备。教程兼顾理论原理与实践操作,提供Python和C++两种实现方案,并涵盖TensorRT优化、实时检测等生产级应用场景,帮助零基础开发者快速掌握边缘AI推理技术,实现高性能目标检测应用。

LXY_LOVE36的博客 367

C++中的智能指针现代C++内存管理的核心利器

这种需求催生了智能指针的概念,它作为资源管理的守护者,旨在将程序员从繁重且易错的内存管理工作中解放出来。当独特指针离开其作用域时,它所管理的对象会自动被销毁,内存也会被释放,这极大地简化了单所有权资源的管理。由于弱指针不保证所指向的对象始终存在,因此在通过弱指针访问对象前,必须检查对象的有效性。为了解决这个问题,弱指针应运而生,它作为共享指针的配套工具,可以观察资源而不增加引用计数,从而打破循环引用。弱指针是一种不控制对象生命周期的智能指针,它是对由共享指针管理对象的非拥有性引用。

xxh2jd的博客 382

第十五章数据管理成熟度评估【6’】(DAMA-CDGA 2022年以后历年模拟题真题汇总,基本包含所有考点。)

第十五章数据管理成熟度评估【6’】(DAMA-CDGA 2022年以后历年模拟题真题汇总,基本包含所有考点。)

JasonH2021的博客 403

C/C++编程中的内存管理从指针陷阱到智能指针的救赎

这解决了`shared_ptr`可能导致的循环引用问题:如果两个对象通过`shared_ptr`相互引用,即使外部已无其他引用,它们的引用计数也无法降为零,从而导致内存泄漏。当使用`new`或`malloc`分配内存后,如果未能使用`delete`或`free`将其释放,这块内存就会一直被程序占用,无法被操作系统回收。智能指针是封装了原始指针的类模板,它通过RAII(资源获取即初始化)技术,将内存的生命周期与对象的生命周期绑定,从而自动化内存管理,极大地减少了内存相关错误的发生。

lawczj的博客 944

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

智能指针在现代C++编程中的核心作用与应用实践

其次,需要根据所有权语义谨慎选择智能指针的类型:明确独占所有权时用`unique_ptr`,需要共享所有权时用`shared_ptr`,并注意搭配`weak_ptr`来规避循环引用。它是一种不控制对象生命周期的智能指针,是`shared_ptr`的“弱引用”。C++11标准的推出是智能指针发展的里程碑,它引入了三种功能各异、职责明确的智能指针:`std::unique_ptr`、`std::shared_ptr`和`std::weak_ptr`,构成了现代C++资源管理的基石。

qq57084818的博客 784

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

【C++】智能指针:内存管理的利器

/ 自定义数组删除器std::cout << "自定义删除器:释放数组\n";// 方式 1:显式指定删除器类型// 方式 2:用 lambda(需要推导类型,C++14 及以上)std::cout << "lambda 删除器:释放数组\n";return 0;本次分享就到这里结束了,至此,c++的学习也就告一段落,后续会继续更新linux学习内容。

2401_85487070的博客 1070
上一篇: C++编程中的内存管理艺术智能指针与RAII原理深度解析
下一篇: 《C++新特性解析现代编程的艺术与科学》
subzero88
博客等级 码龄14年 43粉丝 72原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值