当前位置:文档之家› 虚函数的作用

虚函数的作用

虚函数的作用
虚函数的作用

条款14: 确定基类有虚析构函数

有时,一个类想跟踪它有多少个对象存在。一个简单的方法是创建一个静态类成员来统计对象的个数。这个成员被初始化为0,在构造函数里加1,析构函数里减1。(条款m26里说明了如何把这种方法封装起来以便很容易地添加到任何类中,“my article on counting objects”提供了对这个技术的另外一些改进)

设想在一个军事应用程序里,有一个表示敌人目标的类:

class enemytarget {

public:

enemytarget() { ++numtargets; }

enemytarget(const enemytarget&) { ++numtargets; }

~enemytarget() { --numtargets; }

static size_t numberoftargets()

{ return numtargets; }

virtual bool destroy(); // 摧毁enemytarget对象后

// 返回成功

private:

static size_t numtargets; // 对象计数器

};

// 类的静态成员要在类外定义;

// 缺省初始化为0

size_t enemytarget::numtargets;

这个类不会为你赢得一份政府防御合同,它离国防部的要求相差太远了,但它足以满足我们这儿说明问题的需要。

敌人的坦克是一种特殊的敌人目标,所以会很自然地想到将它抽象为一个以公有继承方式从enemytarget派生出来的类(参见条款35及m33)。因为不但要关心敌人目标的总数,也要关心敌人坦克的总数,所以和基类一样,在派生类里也采用了上面提到的同样的技巧:

class enemytank: public enemytarget {

public:

enemytank() { ++numtanks; }

enemytank(const enemytank& rhs)

: enemytarget(rhs)

{ ++numtanks; }

~enemytank() { --numtanks; }

static size_t numberoftanks()

{ return numtanks; }

virtual bool destroy();

private:

static size_t numtanks; // 坦克对象计数器

};

(写完以上两个类的代码后,你就更能够理解条款m26对这个问题的通用解决方案了。)

最后,假设程序的其他某处用new动态创建了一个enemytank对象,然后用delete 删除掉:

enemytarget *targetptr = new enemytank;

...

delete targetptr;

到此为止所做的一切好象都很正常:两个类在析构函数里都对构造函数所做的操作进行了清除;应用程序也显然没有错误,用new生成的对象在最后也用delete 删除了。然而这里却有很大的问题。程序的行为是不可预测的——无法知道将会发生什么。

c++语言标准关于这个问题的阐述非常清楚:当通过基类的指针去删除派生类的对象,而基类又没有虚析构函数时,结果将是不可确定的。这意味着编译器生成的代码将会做任何它喜欢的事:重新格式化你的硬盘,给你的老板发电子邮件,把你的程序源代码传真给你的对手,无论什么事都可能发生。(实际运行时经常发生的是,派生类的析构函数永远不会被调用。在本例中,这意味着当targetptr 删除时,enemytank的数量值不会改变,那么,敌人坦克的数量就是错的,这对需要高度依赖精确信息的部队来说,会造成什么后果?)

为了避免这个问题,只需要使enemytarget的析构函数为virtual。声明析构函数为虚就会带来你所希望的运行良好的行为:对象内存释放时,enemytank和enemytarget的析构函数都会被调用。

和绝大部分基类一样,现在enemytarget类包含一个虚函数。虚函数的目的是让派生类去定制自己的行为(见条款36),所以几乎所有的基类都包含虚函数。

如果某个类不包含虚函数,那一般是表示它将不作为一个基类来使用。当一个类不准备作为基类使用时,使析构函数为虚一般是个坏主意。请看下面的例子,这个例子基于arm(“the annotated c++ reference manual”)一书的一个专题讨论。

// 一个表示2d点的类

class point {

public:

point(short int xcoord, short int ycoord);

~point();

private:

short int x, y;

};

如果一个short int占16位,一个point对象将刚好适合放进一个32位的寄存器中。另外,一个point对象可以作为一个32位的数据传给用c或fortran等其他语言写的函数中。但如果point的析构函数为虚,情况就会改变。

实现虚函数需要对象附带一些额外信息,以使对象在运行时可以确定该调用哪个虚函数。对大多数编译器来说,这个额外信息的具体形式是一个称为vptr(虚函数表指针)的指针。vptr指向的是一个称为vtbl(虚函数表)的函数指针数组。每个有虚函数的类都附带有一个vtbl。当对一个对象的某个虚函数进行请求调用时,实际被调用的函数是根据指向vtbl的vptr在vtbl里找到相应的函数指针来确定的。

虚函数实现的细节不重要(当然,如果你感兴趣,可以阅读条款m24),重要的是,如果point类包含一个虚函数,它的对象的体积将不知不觉地翻番,从2个16位的short变成了2个16位的short加上一个32位的vptr!point对象再也不能放到一个32位寄存器中去了。而且,c++中的point对象看起来再也不具有和其他语言如c中声明的那样相同的结构了,因为这些语言里没有vptr。所以,用其他语言写的函数来传递point也不再可能了,除非专门去为它们设计vptr,而这本身是实现的细节,会导致代码无法移植。

所以基本的一条是,无故的声明虚析构函数和永远不去声明一样是错误的。实际上,很多人这样总结:当且仅当类里包含至少一个虚函数的时候才去声明虚析构函数。

这是一个很好的准则,大多数情况都适用。但不幸的是,当类里没有虚函数的时候,也会带来非虚析构函数问题。例如,条款13里有个实现用户自定义数组下标上下限的类模板。假设你(不顾条款m33的建议)决定写一个派生类模板来表示某种可以命名的数组(即每个数组有一个名字)。

template // 基类模板

class array { // (来自条款13)

public:

array(int lowbound, int highbound);

~array();

private:

vector data;

size_t size;

int lbound, hbound;

};

template

class namedarray: public array {

public:

namedarray(int lowbound, int highbound, const string& name);

...

private:

string arrayname;

};

如果在应用程序的某个地方你将指向namedarray类型的指针转换成了array类型的指针,然后用delete来删除array指针,那你就会立即掉进“不确定行为”的陷阱中。

namedarray *pna =

new namedarray(10, 20, "impending doom");

array *pa;

...

pa = pna; // namedarray* -> array*

...

delete pa; // 不确定! 实际中,pa->arrayname

// 会造成泄漏,因为*pa的namedarray

// 永远不会被删除

现实中,这种情形出现得比你想象的要频繁。让一个现有的类做些什么事,然后从它派生一个类做和它相同的事,再加上一些特殊的功能,这在现实中不是不常见。namedarray没有重定义array的任何行为——它继承了array的所有功能而没

有进行任何修改——它只是增加了一些额外的功能。但非虚析构函数的问题依然存在(还有其他问题,参见m33)

最后,值得指出的是,在某些类里声明纯虚析构函数很方便。纯虚函数将产生抽象类——不能实例化的类(即不能创建此类型的对象)。有些时候,你想使一个类成为抽象类,但刚好又没有任何纯虚函数。怎么办?因为抽象类是准备被用做基类的,基类必须要有一个虚析构函数,纯虚函数会产生抽象类,所以方法很简单:在想要成为抽象类的类里声明一个纯虚析构函数。

这里是一个例子:

class awov { // awov = "abstract w/o

// virtuals"

public:

virtual ~awov() = 0; // 声明一个纯虚析构函数

};

这个类有一个纯虚函数,所以它是抽象的,而且它有一个虚析构函数,所以不会产生析构函数问题。但这里还有一件事:必须提供纯虚析构函数的定义:

awov::~awov() {} // 纯虚析构函数的定义

这个定义是必需的,因为虚析构函数工作的方式是:最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用。这就是说,即使是抽象类,编译器也要产生对~awov的调用,所以要保证为它提供函数体。如果不这么做,链接器就会检测出来,最后还是得回去把它添上。

可以在函数里做任何事,但正如上面的例子一样,什么事都不做也不是不常见。如果是这种情况,那很自然地会想到将析构函数声明为内联函数,从而避免对一个空函数的调用所产生的开销。这是一个很好的方法,但有一件事要清楚。

因为析构函数为虚,它的地址必须进入到类的vtbl(见条款m24)。但内联函数不是作为独立的函数存在的(这就是“内联”的意思),所以必须用特殊的方法得到它们的地址。条款33对此做了全面的介绍,其基本点是:如果声明虚析构函数为inline,将会避免调用它们时产生的开销,但编译器还是必然会在什么地方产生一个此函数的拷贝。

sizeof计算含有虚函数的类的空间大小

sizeof计算含有虚函数的类的空间大小 当我们计算一种数据类型所占用的空间大小时,很easy,sizeof就可以解决掉。如果我们计算一个类,一个空类,或者一个含有虚函数然后又派生子类时,这时候他们所占用的内存空间是如何变化的呢?下面我们就通过代码来介绍下。 一个不含有虚函数的普通类与其派生类的内存关系 class Base { public: Base(int x):a(x) {} void print() { cout<

2.对于Derived类 Derived类继承于Base类,自然的继承了其成员变量a,自身又扩展了自己的成员变量b,因而多了4个字节。所以Derived类所占用的内存空间大小应该为8字节。 一个含有虚函数的类与其派生类的内存空间占用关系 class A { public: A(int x):a(x){} virtual void print(){cout<

C++ 类的应用

C++考查题 关键词:友员函数成员函数抽象类纯虚函数 一.建立一个复数类imaginary,其私有数据成员x和y表示复数的实部和虚部,构造函数imaginary用于对复数的实部和虚部初始化,成员函数show用于显示复述对象,形式为“实部+虚部i”;友员函数add,sub,mul和div分别用于进行复数的加、减、乘和除法运算。在主函数中,实例化两个复数,并输入一个运算符,按运算符选择相应的友员函数进行复数运算,然后调用成员函数show输出计算结果。 编码实现上述要求并回答以下问题 (1)四个友员函数的形参和返回值分别是什么? (2)四个友员函数可以定义为相应的成员函数吗,写出原型 (3)比较友员函数与成员函数的用法 答:(1)四个友员函数的形参是f1,f2,函数返回值是f (2)四个友员函数可以定义为相应的成员函数,原型如下: 在Imaginary类内的函数声明: Imaginary operator+(Imaginary const&f2); Imaginary operator-(Imaginary const&f2); Imaginary operator*(Imaginary const&f2); Imaginary operator/(Imaginary const&f2); 在Imaginary类外的函数定义: Imaginary Imaginary::operator+(Imaginary const&f2) { Imaginary f; f.x=x+f2.x; f.y=y+f2.y; return f; } Imaginary Imaginary::operator-(Imaginary const&f2) { Imaginary f; f.x=x-f2.x; f.y=y-f2.y; return f; }

C++函数中那些不可以被声明为虚函数的函数

常见的不不能声明为虚函数的有:普通函数(非成员函数);静态成员函数;内联成员函数;构造函数;友元函数。 1、为什么C++不支持普通函数为虚函数? 普通函数(非成员函数)只能被overload,不能被override,声明为虚函数也没有什么意思,因此编译器会在编译时邦定函数。 2、为什么C++不支持构造函数为虚函数? 这个原因很简单,主要是从语义上考虑,所以不支持。因为构造函数本来就是为了明确初始化对象成员才产生的,然而virtual function主要是为了再不完全了解细节的情况下也能正确处理对象。另外,virtual函数是在不同类型的对象产生不同的动作,现在对象还没有产生,如何使用virtual函数来完成你想完成的动作。(这不就是典型的悖论) 3、为什么C++不支持内联成员函数为虚函数? 其实很简单,那内联函数就是为了在代码中直接展开,减少函数调用花费的代价,虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。(再说了,inline函数在编译时被展开,虚函数在运行时才能动态的邦定函数) 4、为什么C++不支持静态成员函数为虚函数? 这也很简单,静态成员函数对于每个类来说只有一份代码,所有的对象都共享这一份代码,他也没有要动态邦定的必要性。 5、为什么C++不支持友元函数为虚函数? 因为C++不支持友元函数的继承,对于没有继承特性的函数没有虚函数的说法。 ********************************************************************* 1、顶层函数:多态的运行期行为体现在虚函数上,虚函数通过继承方式来体现出多态作用,顶层函数不属于成员函数,是不能被继承的。 2、构造函数:(1)构造函数不能被继承,因而不能声明为virtual函数。 (2)构造函数一般是用来初始化对象,只有在一个对象生成之后,才能发挥多态的作用,如果将构造函数声明为virtual函数,则表现为在对象还没有生成的情况下就使用了多态机制,因而是行不通的,如下例:

多继承_虚函数表解析

C++ 虚函数表解析
前言 C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针 指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针 有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可 变的算法。比如:模板技术,RTTI 技术,虚函数技术,要么是试图做到在编译时决议,要么试图 做到运行时决议。 关于虚函数的使用方法,我在这里不做过多的阐述。大家可以看看相关的 C++的书籍。在这篇 文章中,我只想从虚函数的实现机制上面为大家 一个清晰的剖析。 当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的 代码,没有图片,没有详细的说明,没有比较,没有举一反三。不利于学习和阅读,所以这是我 想写下这篇文章的原因。也希望大家多给我提意见。 言归正传,让我们一起进入虚函数的世界。 虚函数表 对 C++ 了解的人都应该知道虚函数 (Virtual Function) 是通过一张虚函数表 (Virtual Table) 来实现的。简称为 V-Table。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、 覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了 这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由 为重要了,它就像一个地图一样,指明了实际所应该调用的函数。 这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在于对象实例 中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承 的情况下) 这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指 。 针,并调用相应的函数。 听我扯了那么多,我可以感觉出来你现在可能比以前更加晕头转向了。 没关系,下面就是实 际的例子,相信聪明的你一看就明白了。 假设我们有这样的一个类: 以下是引用片段: class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } };
按照上面的说法,我们可以通过 Base 的实例来得到虚函数表。 下面是实际例程: 以下是引用片段: typedef void(*Fun)(void);

实验八 虚函数及应用

实验八虚函数及应用 一、实验目的 1.理解虚函数与运行时(动态)多态性之间的关系,掌握虚函数的定义及应用; 2.理解纯虚函数与抽象类的概念,掌握抽象类的定义及应用; 3.理解虚析构函数的概念及作用。 二、实验学时 课内实验:2课时课外练习:2课时 三本实验涉及的新知识 ㈠虚函数与动态多态性 在C++中,如果将基类与派生类的同名成员函数定义为虚函数,就可以定义一个基类指针,当基类指针指向基类对象时访问基类的成员函数,当基类指针指向派生类对象时访问派生类的成员函数,实现在运行时根据基类指针所指向的对象动态调用成员函数,实现动态多态性。换句话说,虚函数与派生类相结合,使C++能支持运行时(动态)多态性,实现在基类中定义派生类所拥有的通用“接口”,而在派生类中定义具体的实现方法,即“一个接口,多种方法”。 ㈡虚函数的定义 1.在基类中定义 在定义函数的前面加上“virtual ”。即: virtual 返回类型函数名(参数表) { …… } 2.在派生类中定义 函数的返回类型、函数名、参数的个数、参数类型及顺序必须与基类中的原型完全相同。 3.说明: ⑴在派生类中定义虚函数时,可用“virtual”也可不用“virtual”(最好都使用)。 ⑵虚函数在派生类中重新定义时,其原型必须与基类中相同。 ⑶必须用基类指针访问虚函数才能实现运行时(动态)多态性;当用普通成员函数的调用方法(即用圆点运算符)调用虚函数时,为静态调用; ⑷虚函数在自身类中必须声明为成员函数(不能为友元函数或静态成员函数),但在另一个类中可以声明为友元函数。 ⑸虚函数可以公有继承多次,其虚函数的特性不变。 ⑹构造函数不能定义为虚函数,但析构函数可以定义为虚函数。 ⑺虚函数与重载函数的关系 ①普通函数重载是通过参数类型或参数的个数不同实现的;重载一个虚函数时,其函数原型(返回类型、参数个数、类型及顺序)完全相同。 ②当重载的虚函数只有返回类型不同时,系统将给出错误信息;如果定义的虚函数只有函数名相同,而参数个数或类型不同时,则为普通函数重载。 ㈢纯虚函数与抽象类 1.纯虚函数定义 格式:

C++实验报告(虚基类)

实验五 Shape虚基类 一、实验目的 1.掌握虚基类的定义、使用 2.理解使用虚函数和继承类实现多态性 二、实验环境 Windows + VC++6.0。 三、实验内容及要求 定义抽象基类Shape,有它派生出三个类:Circle、Rectangle、Triangle,用函数ShowArea 分别显示各种图形的面积,最后还要显示所有的图形面积。要求用基类指针数组,使他的每一个元素指向一个派生类对象。 四、实验步骤 (1) 定义一个虚基类Shape,在ShowArea函数声明前冠以virtual使其成为虚函数,并且能够在派生类中重定义。 (2)定义三个派生类Circle、Rectangle、Triangle,并公有继承Shape基类。

(3)在主函数中分别调用三个派生类的ShowArea函数,求出各个形状的面积。 (4)定义一个基类指针数组p[3]={&A,&B,&C},通过->访问派生类对象

的成员函数ShowArea,如p[0]->ShowArea()访问的是对象A的成员函数。 五、程序分析 1.抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。 2.如果派生类重写了基类的虚函数,那么编译器对应的操作为将指向虚函数表的指针__vfptr指向的函数指针数组中相应的虚函数入口地址改变为当前派生类实现的函数入口地址; 基类指针指向派生类后,实际上指向的是从基类派生到派生类那段成员的首地址(存放__vfptr,如果定义有虚函数),基类指针在调用虚函数的额时候,是通过查该__vfptr地址指向的函数指针数组来查找函数入口地址。所以,如果派生类重写了虚函数,那么以上查找的虚函数的入口地址将是派生类重写的函数的入口地址。 六、实验结果与总结 实验总结: 抽象基类与普通基类不同,他平不是现实存在的对象,它可以没有任何物理上的或其他实际意义方面的含义,例如Shape类,只有一个成员函数,没有数据成员。它既不代表点,也不代表圆。 实验结果如下:

C++虚函数表的工作原理

虚函数表工作原理 C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。 关于虚函数的使用方法,我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中,我只想从虚函数的实现机制上面为大家一个清晰的剖析。 当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的代码,没有图片,没有详细的说明,没有比较,没有举一反三。不利于学习和阅读,所以这是我想写下这篇文章的原因。也希望大家多给我提意见。 言归正传,让我们一起进入虚函数的世界。 虚函数表 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。 这里我们着重看一下这张虚函数表。在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。 听我扯了那么多,我可以感觉出来你现在可能比以前更加晕头转向了。没关系,下面就是实际的例子,相信聪明的你一看就明白了。 假设我们有这样的一个类: class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; 按照上面的说法,我们可以通过Base的实例来得到虚函数表。下面是实际例程: typedef void(*Fun)(void);

c++实验之虚拟函数的使用

实验报告(五)实验名称:多态和虚函数

一、实验目的: 1.掌握虚函数的定义及使用,对多态性的支持; 二、实验内容: 1.设计一个图形类(Shape),由它派生出三角形类(Triangle)、正方形类(Square)、圆形类(Circle),利用虚函数计算图形面积和周长,并在主函数中进行测试。 2.定义一个教师类,由教师类派生出讲师、副教授、教授类。教师的工资分别由基本工资、课时费和津贴构成。假设讲师、副教授、教授的基本工资分别为500、600、900元,课时费分别为每小时20、30、50元,津贴分别为300、500、1000。定义虚函数来计算教师的工资,并通过主函数来进行验证。 三、实验过程及记录: 按实验要求分别派生出相应的函数,然后在主函数中定义类的指针,分别对相应的虚函数扫描一遍输出结果即可 四、实验结果及分析: 要知道虚函数虚函数构造和在派生类中调用的格式,另外需注意派生类名称后面只有一个“:”号,再要注意主函数中如何用类型的指针调用函数输出结果的 附源码: 1的源码: #include #include using namespace std; class shape { public: virtual void Area() { cout<<"这是基类shape的求周长和求面积的函数"<>a>>b>>c; p=a+b+c; s=sqrt(p*(p-a)*(p-b)*(p-c)); cout<<"三角形周长为:"<

实验十多态性—虚函数的应用 - 参考答案

实验十多态性—虚函数的应用10.1 实验目的 1.理解和掌握虚函数的作用; 2.掌握利用虚函数实现C++的运行时多态性; 3.理解纯虚类和抽象类。 10.2 实验内容 10.2.1程序阅读 1.理解下面的程序,并运行查看结果,回答程序后面的问题。 #include using namespace std; class Point { public: Point(double i,double j) { x=i; y=j; } double Area() const{return 0;} private: double x,y; }; class Circle:public Point { public: Circle(double i,double j,double k); double Area() const {return 3.14*r*r;} private: double r; }; Circle::Circle(double i,double j,double k):Point(i,j) { r=k; }

void fun(Point &s) { cout< using namespace std; class A { public: virtual void f(void) { cout<<"f function of class A is called! \n"; } }; class B:public A { public: void f(void) { cout<< "f function of class B is called! \n"; } }; class C:public B { public: void f(void) { cout<< "f function of class C is called! \n"; }

Virtual详细用法

virtual用法一 #include using namespace std; class A{ public: virtual void display(){ cout<<"A"<display(); delete p; } int main(int argc,char* argv[]) { doDisplay(new B()); return 0; } 这段代码打印出的结果为B,但是当把A类中的virtual去掉之后打印出的就为A。当基类中没有virtual的时候,编译器在编译的时候把p看做A类的对象,调用的自然就是A类的方法。但是加上virtual之后,将dispaly方法变成了虚方法,这样调用的时候编译器会看调用的究竟是谁的实例化对象,这样就实现了多态的效果。也就是说,当基类的派生类中有重写过基类的虚方法的时候,使用基类的指针指向派生类的对象,调用这个方法实际上调用的会是派生类最后实现的方法 virtual用法二 #include using namespace std; class Person{ public: Person(){ cout<<"Person构造"<

相关主题
文本预览
相关文档 最新文档