【VC++】虚函数 内存结构 - 第三篇(单继承,有虚函数覆盖)

C++内存分布 虚表 虚指针(非常重要) C++内存分布 虚表 虚指针: class Base { public: int m_base; }; class DerivedA: public Base { public: int m_derivedA; }; class DerivedB: public Base { public: int m_derivedB; }; class D 阅读详情

【VC++】虚函数 内存结构 - 第三篇(单继承,有虚函数覆盖)

#include <IOSTREAM>
using namespace std;

class Base
{
public:
	int nBase1;
	int nBase2;
	
	Base(int n1,int n2):nBase1(n1),nBase2(n2)
	{
		cout<<"Base::Base("<<n1<<","<<n2<<")"<<endl;
	}
	
	virtual void F()
	{
		cout<<"Base::F()"<<endl;
	}
	virtual void G()
	{
		cout<<"Base::G()"<<endl;
	}
};

class Derive:public Base
{
public:
	int nBase1;	//与父类成员变量名一样
	int nDerive2;
	
	Derive(int n1,int n2,int n3,int n4):Base(n1,n2),nBase1(n3),nDerive2(n4)
	{
		cout<<"Derive::Derive("<<n3<<","<<n4<<")"<<endl;
	}
	
	virtual void F()	//覆盖父类的虚函数
	{
		cout<<"Derive::F()"<<endl;
	}
	virtual void G2()
	{
		cout<<"Derive::G2()"<<endl;
	}
};


typedef void(*Fun)(void);

void TestBase(Base &b)
{
	Fun pFun = NULL;
	int** pVtab = (int**)&b;
	
	cout << "类对象地址 / 虚函数表指针的地址:" << (&b) << " / " << pVtab << endl;
	cout << "虚函数表指针值(虚函数表的地址)(十进制):" << (int)pVtab[0] <<endl;
	cout << "虚函数表指针值(虚函数表的地址)(十六进制):" << pVtab[0] <<endl; 
	
	cout << "虚函数表 — 第 1 个函数地址的地址:"  <<  &pVtab[0][0] << " , ";	//好比一个变量的地址
	cout << "函数地址值:"  << (int*)pVtab[0][0] << " 调:";	//好比一个变量的值(这个值也是个地址)
	pFun = (Fun)pVtab[0][0];
	pFun();
	
	cout << "虚函数表 — 第 2 个函数地址的地址:"  <<  &pVtab[0][1] << " , ";
	cout << "函数地址值:"  << (int*)pVtab[0][1] << " 调:";
	pFun = (Fun)pVtab[0][1];
	pFun();
	
	cout << "虚函数表 — 第 3 个函数地址的地址:"  <<  &pVtab[0][2] << " , ";
	cout << "函数地址值:"  << (int*)pVtab[0][2] <<endl ;	//如果仅有一个虚函数,后面的节点不是NULL???
	//pFun = (Fun)pVtab[0][2];
	//pFun();
	
	cout << "类对象 — 第 1 个变量地址:" << &pVtab[1]  << " 值:"<< (int)pVtab[1] <<endl;	
	cout << "类对象 — 第 2 个变量地址:" << &pVtab[2]  << " 值:"<< (int)pVtab[2] <<endl;
	cout << "类对象 — 第 3 个变量地址:" << &pVtab[3]  << " 值:"<< (int)pVtab[3] <<endl;	//之后的内存是什么???
	
	cout<<endl;
}

void TestDerive(Derive &d)
{
	Fun pFun = NULL;
	int** pVtab = (int**)&d;
	
	cout << "类对象地址 / 虚函数表指针的地址:" << (&d) << " / " << pVtab << endl;
	cout << "虚函数表指针值(虚函数表的地址)(十进制):" << (int)pVtab[0] <<endl;
	cout << "虚函数表指针值(虚函数表的地址)(十六进制):" << pVtab[0] <<endl; 
	
	cout << "虚函数表 — 第 1 个函数地址的地址:"  <<  &pVtab[0][0] << " , ";	//好比一个变量的地址
	cout << "函数地址值:"  << (int*)pVtab[0][0] << " 调:";	//好比一个变量的值(这个值也是个地址)
	pFun = (Fun)pVtab[0][0];
	pFun();
	
	cout << "虚函数表 — 第 2 个函数地址的地址:"  <<  &pVtab[0][1] << " , ";
	cout << "函数地址值:"  << (int*)pVtab[0][1] << " 调:";
	pFun = (Fun)pVtab[0][1];
	pFun();

	cout << "虚函数表 — 第 3 个函数地址的地址:"  <<  &pVtab[0][2] << " , ";
	cout << "函数地址值:"  << (int*)pVtab[0][2] << " 调:";
	pFun = (Fun)pVtab[0][2];
	pFun();
	
	cout << "虚函数表 — 第 4 个函数地址的地址:"  <<  &pVtab[0][3] << " , ";
	cout << "函数地址值:"  << (int*)pVtab[0][3] <<endl ;	//如果仅有一个虚函数,后面的节点不是NULL???
	
	cout << "类对象 — 第 1 个变量地址:" << &pVtab[1]  << " 值:"<< (int)pVtab[1] <<endl;	
	cout << "类对象 — 第 2 个变量地址:" << &pVtab[2]  << " 值:"<< (int)pVtab[2] <<endl;
	cout << "类对象 — 第 3 个变量地址:" << &pVtab[3]  << " 值:"<< (int)pVtab[3] <<endl;
	cout << "类对象 — 第 4 个变量地址:" << &pVtab[4]  << " 值:"<< (int)pVtab[4] <<endl;
	cout << "类对象 — 第 5 个变量地址:" << &pVtab[5]  << " 值:"<< (int)pVtab[5] <<endl;	//之后的内存是什么???
	
	cout<<endl;
}

void main()
{
	Base b(111,222);
	Derive d(123,321,456,654);

	TestBase(b);
	TestDerive(d);

	cout<<"d.nBase1: "<<d.nBase1<<endl;
	cout<<"d.Base::nBase1: "<<d.Base::nBase1<<endl;

	cout<<endl;
}

运行结果:
在这里插入图片描述
父类对象、子类对象,各种地址和值的比对(红色表示不同的地方):

在这里插入图片描述
类图、监视值(可以看到,VS的监视值并不全面,还是以实际打印的为准):
在这里插入图片描述
在内存中的结构是:
在这里插入图片描述
在本文结束前,我也还是来介绍下自己吧。我从03年初学编程,一直到现在都在做软件开发方面的事,做过VC++、VB/Delphi/C++Builder、C#/Asp.Net、Java、Web前后端、数据库、服务器端、客户端、js、lua、Flash AS、cocos2dx/Unity3d……都有接触过,甚至做过很长一段时间的运营、产品策划、美工、客服、线下推广员、公司经理 多职务……技术只是一种工具,用来实现业务需求,所以最大的特长是逻辑思维、挖掘业务需求、强化用户体验、快速的产品实现,最自以为豪的,我做过的很多产品,很稳定,bug极少(当然也出现过没考虑周全的漏洞在所难免但总体上也还算少),有很多独立开发的游戏运营到现在还在稳定的运行。不是技术极客,而是业务需求带动技术的学习研究,所以也不怎么挑语言,编程语言大体上也都是相通的,底层原理也有很多类同的地方。所以缺点从不敢说精通哪一门语言。遇到小工具、小需求的订制实现,我个人最喜欢的还是用VC6,简洁干净,编译出来的东西 体积小,速度快,占CPU内存小。当然实际工作中,还是随项目。欢迎大家和我交流,我的QQ是:七6.肆-陆_柒-4`7_四.
生活中的我,热爱 街舞、飙车、花式游泳、游戏一条命通关……极限运动,我抖音号(一不小心玩出花样)是:1917940952

这样,我们就可以看到对于下面这样的程序:

	Base *pBase = new Derive(1,2,3,4);
	pBase->F();

由 pBase 所指的内存中的虚函数表的 F() 的位置已经被 Derive::F() 函数地址所取代,于是在实际调用发生时,是 Derive::F() 被调用了。这就实现了多态。

聪明的你,肯定马上想到这样来试试:

	Base *pBase = new Derive(1,2,3,4);
	pBase->Base::F();

根据打印结果,发现是 Base::F() 调用了,刚不是说被覆盖了吗,怎么还能调用?
注意,父类有一个虚函数表(假设为表A),子类也有一份虚函数表(假设为表B),先用父类的虚函数铺设 表B,然后再把子类的虚函数在 表B 中,进行 覆盖、追加,但并没有影响到 表A。即表A和表B,是两块不同的内存空间,如果特意指明用Base域,那么其实访问的是 表A的 函数地址,于是就调用了 Base::F(),当然,表A和表B中,有两个不同的地址,存放了一样的函数地址Base::G():

在这里插入图片描述
再拓展一下,一个类一旦定义了一个虚函数(先不考虑“纯虚函数”情况),不管怎么派生,它的虚函数代码块,已经存放在内存中,至于谁的虚函数表里有没有存放这个虚函数,也只是有没有存放这个虚函数的地址,所谓的“虚函数覆盖”,也只是虚函数表里的“函数地址”覆盖,但并不是代码块的覆盖!这一点是网上很多关于“虚函数覆盖”的文章,几乎都没有提及的!

那么本文的代码示例,更详尽的内存结构图是:
在这里插入图片描述
关于绿框中的描述,如果有兴趣研究,请看这篇文章:
【VC++】虚函数 内存结构 - 第三篇(篇外篇)(类有无实体对象,对虚函数表的影响研究)

感谢你有耐心看到这里,那这里留个作业题,如果不靠编译运行看结果,你能推断出下面这段代码的打印结果吗?

#include <IOSTREAM>
using namespace std;

class Base
{
public:
	int n;
	
	virtual void SetN(int nPara)
	{
		n = nPara;
	}
};

class Derive:public Base
{
public:
	int n;	//同父类
	
	virtual void SetN(int nPara)	//同父类
	{
		n = nPara;
	}

	void PrintN()	//打印父类、子类的 n
	{
		cout<<"Base::n : "<<Base::n << " , Derive::n : "<<n<<endl;
	}

	void Reset()
	{
		Base::n = 0;
		n = 0;
	}
};

void main()
{
	Base *pBase = new Derive;
	Derive * pDerive = (Derive *)pBase;

	pDerive->Reset();
	pDerive->PrintN();//打印:Base::n : 0 , Derive::n : 0
	cout<<endl;
	

	pBase->Base::SetN(1);
	pDerive->PrintN();//作业题(1)这里会打印什么?为什么?

	pDerive->Reset();
	pBase->SetN(2);
	pDerive->PrintN();//作业题(2)这里会打印什么?为什么?

	pDerive->Reset();
	pDerive->Base::SetN(3);
	pDerive->PrintN();//作业题(3)这里会打印什么?为什么?
	
	pDerive->Reset();
	pDerive->SetN(4);
	pDerive->PrintN();//作业题(4)这里会打印什么?为什么?

	delete pDerive;
	cout<<endl;
}

(转载时请注明作者和出处。未经许可,请勿用于商业用途)
原创出处:https://blog.youkuaiyun.com/maoyeahcom/article/details/108775609

Java农产品质量溯源大数据管理系统源码 产品追溯基于“一物一码”从工厂生产环节至销售阶段,赋予产品唯一的数码,并关联产品名称、批号、生产时间等信息,建立包装关联关系,为后期产品的追溯、营销搭建信息基础。 农产品溯源,从原材料收购、生产、加工质检到包装运输、终端门店销售等环节追溯,可以保证产品质量,监管部门和消费者都可以查询产品信息。对生产进行层层把关、一旦发现问题、立即进行处理、提升产品质量。 阅读详情

相关推荐

Mamba vs YOLO:目标检测选型指南(附实际项目对比)

本文深入对比了Mamba和YOLO在目标检测领域的性能差异与应用场景。通过实际项目数据,分析了两者在城市交通监控、工业质检等场景下的表现,提供了详细的选型指南和部署优化建议,帮助开发者根据实时性、精度等需求选择最适合的解决方案。

weixin_29062255的博客 368

虚函数打印-单继承无函数覆盖

虚函数打印-单继承无函数覆盖 struct func1 { public: virtual void Box1() { printf("Box1\n"); } virtual void Box2() { printf("Box2\n"); } }; struct func2 : func1 { public: virtual void Box3() { printf("Box3\n"); } virtual void Box4() { printf("Box4\n");

weixin_42408832的博客 203

教你读懂techpoint视频解码芯片tp2855规格书(第一部分)

所有的控制环都是可编程的,以实现最大化的灵活性。对于NTSC/PAL输入,输出时钟和采样结构是非标准的,以便在多路复用输出时与HD格式一同工作。所有通道的音频数据都可以选择性地级联,以实现最大的灵活性。TP2855是一款通用的高清/标清视频解码器芯片,其支持对HD-TVI、NTSC/PAL制式960H分辨率及普通AHD视频格式的解码。TP2855具有内部钳位,在每个通道上都有自动增益控制放大器,用于最佳的信号调节。即使在使用低质量和长线缆的条件下,它的可编程均衡放大器和自动控制环也能维持尚佳的表现。

hufan92的博客 4918

【VC++】虚函数 内存结构 - 第二篇(单继承,无虚函数覆盖)

【VC++】虚函数 内存结构 - 第二篇(单继承,无虚函数覆盖) #include <IOSTREAM> using namespace std; class Base { public: int nBase1; int nBase2; Base(int n1,int n2):nBase1(n1),nBase2(n2) { cout<<"Base::Base("<<n1<<","<<n2<<")"<<end

maoyeahcom的博客 253

别再只用GPS了!用Android手机传感器实现室内步行轨迹记录(避坑指南)

本文详细介绍了如何利用Android手机传感器(如加速度计、陀螺仪和磁力计)实现室内步行轨迹记录,克服GPS在室内的局限性。通过传感器数据采集、步态检测、航向估计和轨迹绘制等步骤,结合避坑指南和优化技巧,帮助开发者高效构建室内定位应用。

weixin_26821003的博客 274

得利捷datalogic扫码枪调试说明

针对得利捷datalogic扫码枪快速调试并,可以根据详细步骤设置扫码枪,并配有tcp调试助手辅以验证,以达到快速调试完成扫码枪的工作。

Conway510的博客 1万+

C++ 虚函数表原理和类的内存分布

C++ 虚函数表原理和类的内存分布

恋恋风辰的技术博客 1935

鸿蒙之HML语法参考

HML(HarmonyOS Markup Language)是一套类HTML的标记语言,通过组件,事件构建出页面的内容。页面具备数据绑定、事件绑定、条件渲染和逻辑控制等高级能力。

2401_82772199的博客 1649

虚函数的内存结构

虚函数的内存结构 虚函数的内存结构 一、 基本概念     C++中的虚函数的作用主要是实现了多态的机制。     关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。     所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RT

那些年 1199

DBeaver 实战:手动配置 PostgreSQL JDBC 驱动的高级技巧

本文详细介绍了在DBeaver中手动配置PostgreSQL JDBC驱动的高级技巧。针对企业内网、多版本数据库兼容等场景,从获取官方驱动文件、创建自定义驱动配置,到使用Maven坐标管理、连接参数调优及解决常见连接问题,提供了完整的实战指南,帮助开发者实现对数据库连接环境的精准掌控。

apple5的专栏 837

工业视觉降本300%实战:C# + 开源YOLO如何干掉Halcon/VisionPro?

在当前制造业普遍追求“降本增效”的大背景下,工业视觉检测系统作为产线的眼睛,其成本问题日益突出。通过该方案,我们成功帮助一家电子厂将单条产线的视觉检测成本从15万降低至不足5千(主要为工控机硬件成本),降本幅度超过95%,且系统稳定性经受住了6个月连续生产的考验。:C#负责“工程化”和“业务逻辑”,YOLO负责“智能算法”,二者结合,完美覆盖了工业视觉从算法到落地的全链路。的组合,构建一套高性能、低成本、易维护的工业视觉解决方案,并彻底替代昂贵的付费库。” 这是一个典型的误区。工业相机/USB相机。

2601_94871597的博客 429

C++编程进阶:虚函数内存结构与菱形继承解析

这是对 C++ 中虚函数与菱形继承内存结构的详细解释。虚函数通过vtable实现多态,而虚继承解决了多重继承中的“菱形问题”,确保各级继承间的一致性和共享单个基类实例。菱形继承是在继承层次中,一个类从两个基类继承,而这两个基类又继承自同一个顶级基类。

m0_72877724的博客 646

扩散模型中的UNET

一、为什么UNET模型可以用于去噪网络 二、扩散模型中的UNET是一个条件去噪网络,怎么实现的 三、UNET用于分割和用去去噪的区别

m0_37737957的博客 3816

【VC++】虚函数 内存结构 - 第四篇(多重继承,无虚函数覆盖)

【VC++】虚函数 内存结构 - 第四篇(多重继承,无虚函数覆盖)

maoyeahcom的博客 266

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

【VC++】虚函数 内存结构 - 第五篇(多重继承,有虚函数覆盖)

【VC++】虚函数 内存结构 - 第五篇(多重继承,有虚函数覆盖)

maoyeahcom的博客 254

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Vc++内存布局

Vc++内存布局 测试平台 Windows server 2012 R2 and visual studio 2013 professional。 本篇文章意在介绍vc++中类的内存布局方式,只是研究其大概的排列方式,对于内存对齐方面则没有进行深入探讨。如有需要研究类的具体大小可以参考其他博客。 测试方式 在vs中,查看类的内存布局主要有两种方式。一种是实例化一个类,然后在de...

antangzhang9759的博客 197

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

【VC++】虚函数 内存结构 - 第一篇(单类)

前言 - 为何写此系列文章? 网上讨论虚函数内存结构的文章很少,好不容易有几位大神写了几篇很精彩的文章,结果里面还有错误: 陈皓大神的《C++ 虚函数表解析》: https://blog.youkuaiyun.com/haoel/article/details/1948051 《C++ 对象的内存布局(上)》: https://blog.youkuaiyun.com/haoel/article/details/3081328 《C++ 对象的内存布局(下)》: https://blog.youkuaiyun.com/haoel/article

maoyeahcom的博客 350

VC_VC++类继承内存布局

上看到了一篇写得非常好的文章,是有关c++类继承内存布局的。看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用。 ——谈VC++对象模型 (美)简.格雷 程化    译 译者前言 一个C++程序员,想要进一步提升技术水平的话,应该多了解一些语言的语意细节。对于使用VC++的程序员来说,还应该了解一些VC++对于C++的诠释。 Inside the C++ Object

优快云-MarkYang 2541

Windows软件开发实践:C++虚函数与虚函数表

摘要:本文深入探讨了C++虚函数表(vtbl)的实现机制及其在继承中的特性。虚函数表是存储虚函数地址的指针数组,位于类对象内存起始处,所有对象共享同一虚表。文章通过代码实例验证了虚表的存在和访问方式,并分析了5种继承场景下的虚表布局:(1)基类、(2)单继承无覆盖、(3)单继承有覆盖、(4)多重继承无覆盖、(5)多重继承有覆盖。实验表明,派生类会继承基类虚表,重写虚函数会替换对应表项,多重继承会产生多个虚表。研究揭示了C++多态性的底层实现原理。

fairyfar的专栏 583

再谈带有虚函数的类对象之内存结构

前篇:http://blog.youkuaiyun.com/pathuang68/archive/2009/04/21/4096521.aspx 下面的代码中,类Base中定义了两个虚函数vfBase_1()和vfBase_2(),另外还定义了一个整形成员变量m_base;#include using namespace std; class Base{public:   

玄机逸士的专栏 1729

[转载]C++继承与内存分配,虚基类指针vbptr和虚函数指针vfptr

转载:继承 内存分布 原作者:DylanWind class Base { public: int m_base; }; class DerivedA: public Base { public: int m_derivedA; }; class DerivedB: public Base { public: int m_deriv...

JMcc_的博客 887

C++基础 虚函数

析构函数需要为虚函数, 默认静态联编, delete会调用指针类型的析构函数, 释放派生类对象中基类指向的内存, 不会释放派生类指向的内存。析构函数时虚的, 则会先调用对象析构函数先释放派生类指向的内存, 再调用基类析构函数释放基类指向的内存。友元不能是虚函数, 友元不是类成员, 只有成员才能是虚函数, 可以用过友元函数使用虚成员函数来解决。派生类有两个虚函数表, 根据继承先后排列, 派生类的虚函数添加到第一个继承基类的虚函数表中。会发现同一类的对象用的虚函数是相同的, 都指向一个指针。

zbbzb的博客 1397

C++多态底层机制:从虚函数表到内存布局的完整解析

面向对象编程中的多态性是实现代码复用和扩展性的核心技术,它允许程序在运行时根据对象的实际类型调用相应的方法。其核心原理是通过虚函数表实现动态绑定,编译器为每个包含虚函数的类生成一个函数指针数组,对象通过虚函数表指针间接调用正确的函数实现。这一机制虽然引入轻微的性能开销,但为软件设计带来了极大的灵活性,广泛应用于框架设计、插件系统和算法策略等场景。本文深入剖析C++中虚函数表的内存布局和汇编级调用过程,并探讨虚析构函数的必要性,帮助开发者从底层理解多态的实现机制。

weixin_30664615的博客 402

c++继承中的内存布局

 今天在网上看到了一篇写得非常好的文章,是有关c++类继承内存布局的。看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用。 ——谈VC++对象模型(美)简.格雷程化    译 译者前言 一个C++程序员,想要进一步提升技术水平的话,应该多了解一些语言的语意细节。对于使用VC++的程序员来说,还应该了解一些VC++对于C++的诠释。 Inside the C++ Object

cesc 1万+

【C++ 篇 7】类的大小由什么决定?内存对齐、虚函数和纯虚函数的区别

内存对齐就是为了让内存存取更加有效率而采取的一种优化手段,对齐的结构是使得内存中数据的首地址是CPU单次获取数据大小的整数倍。

2401_85061293的博客 1030

【VC++】虚函数 内存结构 - 第三篇(篇外篇)(类有无实体对象,对虚函数表的影响研究)

本文只是研究,并不一定正确,更不会很系统全面。若有错误,还请斧正! 先贴上 完整代码: #include <IOSTREAM> //原创:https://blog.youkuaiyun.com/maoyeahcom/ #include <Windows.h> //转载请指明出处,请勿用于商业用途 using namespace std; //仅用于研究,不权威,可能存在错误 class Base{ public: virtual void F(){ cout<<"Base::F

maoyeahcom的博客 274
上一篇: 【VC++】虚函数 内存结构 - 第二篇(单继承,无虚函数覆盖)
下一篇: 【VC++】虚函数 内存结构 - 第三篇(篇外篇)(类有无实体对象,对虚函数表的影响研究)
maoyeahcom
博客等级 码龄15年 14粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值