当前位置:首页 > 娱乐

接口设计 关于接口的设计与声明--对封装性的理解

所谓软件设计,就是“让软件做你想让它做的事情”的步骤和方法,通常是以一个比较笼统的思路开始,以充分的细节结束,以允许特殊接口的开发。这些接口必须转换成声明性的。本文讨论了一个好的C++接口的设计和声明。

1.使界面易于正确使用,不容易被误用

C++有很多接口,比如函数接口、类接口、接口...每个接口都实现了客户与您的代码交互的方式。理想情况下,客户将始终准确地使用您的接口,并获得理想的结果,但是如果客户不正确地使用接口,就不应该编译代码。

用结构体限制参数类型

假设我们现在需要创建一个表示时间的类

class { public: Date(int month,int day,int year);...};

乍一看,这个班没什么问题,但其实有很多隐患。我们当然希望用户能够准确的使用我们的类,但是用户可能会因为一些特定的原因,比如没有按照月、日、年的顺序完成建设,而不能正确的使用我们的类。此时,为了防止用户出错,我们需要按照我们的设计强制用户使用这个类:

//特殊设计//默认情况下,struct内部受公共访问限制。struct day { ExplicItDay(int d):val(d){ } int val;};struct Mountain { ExplicItMountain(intm):val(d){ } int val;};structure year { ExplicItyEar(int y):val(d){ } int val;};上课日期{公共:日期(月& ampm,constDay & ampd,constYear & ampy);...};日期D1(3031996);//错误!日期d2(月(3),日(30),年(1996));//对!

用结构封装数据可以明智谨慎地导入新类型,防止“接口误用”。

类型一旦限定,限定其值也是合理的。比如一年只有12个月,那么Month应该反映这一点。一种方法是用表示月份,但是enum没有我们想要的类型安全性,例如,它可以用作int。预定义所有有效的月份更安全。

class month { public:month Jan { return month(1);} staticMonthFeb { return month(2);} ....staticMonthDec { return month(12);}:ExplicItMount月(intm);..};(年月日(30),年(1996));

用函数代替对象来表示一个特定的月份是一个很好的方法。

限制类型内可以和不可以做的事情

除非有更好的理由,尽量让你的类型表现得和内置类型一样!

用户很清楚int这样的类型的行为,所以你要在合理的前提下尽量让你的类型有相同的操作。例如,如果a和b都是int,则分配a * b是非法的。

避免与内置类型无缘无故的不兼容。真正的原因是= =提供行为一致的接口= =。很少有其他属性能比“一致性”更能导致“接口被正确使用”,也很少有其他属性比“不一致性”更能加剧接口的恶化。

2.设计类就像设计类型

C++和其他OOP语言一样。定义新类时,就定义了新类型。包括,重载函数和运算符,控件的分配和返回,定义对象的初始化和解构,都在你的控制之下,所以你在设计类的时候要像“语言设计者设计语言内置类型的时候”一样用心。下面给出了一些类设计规范。

新类型的对象应该如何创建和销毁?这次是学习如何设计类构造函数和内存分配函数以及释放函数的时候了。

对象初始化和对象赋值有什么区别?这决定了如何设计构造函数和赋值运算符。最重要的是不要把“初始化”和“赋值”混为一谈,因为它们对应的是不同的函数调用。

如果通过值传递新的类型对象意味着什么?请记住,复制构造函数用于定义如何实现类型的传递值。

新型的“法律价值”是什么?这意味着必须检查成员函数的错误,这也会影响函数抛出的异常和函数异常列表。

你的类型需要和继承系统合作吗?如果您从一些现有的类继承,您将被这些类的设计所约束,尤其是因为它们的函数是虚拟的或非虚拟的。如果你允许你的类被其他类继承,这将影响你的析构函数是否是虚拟的。

你的新类型需要什么转型?因为您的类型存在于大量其他类型中,所以它决定了您是否需要有一种方法将您的类型转换为其他类型(隐式还是显式?)

这种新型有哪些合理的运算符和函数?这取决于您的成员函数的设计。

应该拒绝哪些标准函数?这些是你声明为私有的对象。

谁应该带新类型的成员?这就决定了函数是公有的还是私有的怎么安排,哪些函数/类是友元。

新类型的“未声明接口”是什么?他为效率、异常安全、资源利用提供了什么保障?

你的新类型有多一般?如果要定义整个类型族而不是新类型,则应该定义一个新的类模板。

真的需要新型号吗?如果只是想给基类增加一些函数,最好定义一个或者多个非成员函数或者模板。

设计一个类是一个很有挑战性的任务,所以如果你想设计一个类,就像设计一个类型,想好各种问题。

3.将传递值替换为传递常量

默认情况下,C++总是以按值传递的方式将对象传递给函数。事实上,它传递由复制构造函数生成的副本,这可能会使按值传递成为一项昂贵且耗时的操作。

问题产生

类Person { public:Person;虚拟~人;...private:::字符串名称;std::字符串地址;};班级学生:公众人物{公众:学生;~学生;...private:STD::string School name;STD::string School ADdress;};//inmain: bool checkStudent(学生s);学生一;bool whoh = checkStudent(一个);

当checkStudent被调用时会发生什么?

这显然是一个按值传递的函数,这意味着将出现复制构造函数。对于这个函数,参数的传递代价是“学生复制构造函数的一次调用加学生析构函数的一次调用”。不仅如此,学生也是从人继承的,所以也有一个人构造器和一个人析构器,还有学生里面的两个字符串对象和人里面的两个字符串对象。总之,总成本是“六个构造函数和六个析构函数”多么可怕的费用!

问题解决

解决这个问题很简单。只需使用通过引用传递给const。因为按引用不会导致构造函数和析构函数的使用,所以节省了很多开销,而且因为是const,所以也保证了函数中的参数不会改变。

boolcheckStudent(const student & amp;s);

问题生成2

传递值也会导致对象切片。当一个derevedclass对象被值传递并被视为基类对象时,bass类的复制构造函数将被调用,所有“导致该对象表现得像派生类对象”的专门化属性将被切断,只剩下基类对象。这并不奇怪。

类窗口{公共:...STD::strignameconst;virtualdisplayconst};class SpecialWindow {public:..virtualvoiddisplayconst};....//在main:void print(Window w){ cout & lt;& ltw . name;w .显示器;}

当你传递一个SpecialWindow对象到void print(Window w)函数时,如上所述,SpecialWindow的所有特殊属性都会被切断。因此,您想要输出SpecialWindow的特殊内容,但只输出窗口内容。

问题解决2

这个问题的解决方法还是借鉴。因此触发动态绑定,使用SpecialWindow的显示。

无效打印(常量窗口& ampw){ cout & lt;& ltw . name;w .显示器;}

总结

偷窥C++的底层会发现,其实引用是通过指针实现的,通过引用传递通常意味着真正传递的是指针。因此,如果您有一个内置类型的对象(如int),传递值通常更有效。STL迭代器和函数对象也是如此。因为它们都被设计成传递值。迭代器和函数对象的实践者有责任看看它们是否有效,是否没有切割问题。

有些人认为所有的小类型对象都应该应用按值传递,即使对于用户定义的类也是如此。其实并不准确。第一,因为对象小,不代表他的复制构造器成本小。2)即使小对象没有昂贵的复制构造器,也可能存在效率问题。比如有些编译器不愿意把一个只有一个double组成的对象放入缓冲区,但是如果你使用引用,编译器肯定会把指针(也就是引用的实现者)放入缓冲区。3)作为用户定义的类型,其大小可以很容易地改变。随着不断的使用,物体可能会越来越大。

一般来说,假设“按值传递更合适”的唯一对象是迭代器和内置类型的函数对象以及STL是合理的,其他的还是引用使用比较好。

4.当你必须返回一个对象时,不要试图返回它的引用

前面,我们讨论了通过引用传递可以提高效率。结果,一些人开始坚定地使用引用,甚至开始将一些引用点传递给不存在的对象。

问题产生

这个问题的原因很简单,就是作者希望节省开支,提高效率。从而产生大量的错误。

class { public:Rational(int num 1 = 0 int num 2 = 1);...private:intn1,N2;理性的朋友& amp*(建设& amplhs,constRational & ampRHS);

Operator*试图返回一个引用,并为其寻找一个逻辑实现代码。

试试1:直接回去

理性&。运算符*(构造& amplhs,constRational & ampRHS){ Rational result(lhs . n1 * RHS . n1,lhs . N2 * RHS . N2);returnresult}

问题很明显。因为结果是堆栈对象上的,对象在作用域完成后被销毁,所以返回一个没有指向的对象。尝试失败!

尝试2:返回堆对象

理性&。运算符*(构造& amplhs,constRational & ampRHS){ Rational * result = NewRational(lhs . n1 * RHS . n1,lhs . N2 * RHS . N2);return * result}

乍一看,这个代码似乎没有问题,但实际上隐含着谋杀。您在函数中动态请求一个内存块来存储这个变量,这意味着您必须管理这个资源(参见上面的资源管理)。然而,管理这个资源几乎是不可能的,因为您不能指望主函数中的一个变量来监视这个资源并及时删除它。而且当*运算符被大量使用时,是不可能管理大量资源的!即使你有这样的毅力和管理,也不可能指望用户愿意做这样的体力活。

尝试3:使用静态变量

理性&。运算符*(构造& amplhs,constRational & ampRHS){ StatiRational结果(lhs.n1 * rhs.n1,lhs . N2 * RHS . N2);returnresult}

乍一看,这段代码似乎又要成功了?!其实没有。问题很隐蔽:

bool运算符= =(const Rational & amp;理性常数。RHS);if((a*b) == (c*d)) {...}else{...}

问题出在等号的运算上,永远成立!因为,在调用operator ==之前,已经调用了两个运算符,每个运算符都返回操作函数内部的对象,而这两个对象实际上是一个对象!(对于打电话的人来说,是真的!结果你根本没有完成*运算符应该有的功能。

问题解决

问题的解决方法是,不要再纠结了!使用传递值。只是构造函数和。相比很多失误和管理。这个费用还是很划算的。

class Rational { public:Rational(int num 1 = 0 int num 2 = 1);...private:intn1,N2;朋友推理运算符*(构造& amplhs,constRational & ampRHS){ returnRational(lhs . n1 * RHS . n1,RHS . N2 * RHS . N2);}5.将成员变量声明为私有

当我们第一次学习C++ OOP时,有一个规则,成员变量应该总是声明为私有的。在这一节中,我们讨论为什么成员变量应该声明为私有的。

原因一:语法一致性。

因为成员变量不是公共的,所以客户访问对象的唯一方式是通过成员函数。如果公共接口中的一切都是函数,那么客户在调用时就不用担心是否要用括号了。这样可以节省很多时间。

原因二:使用函数可以让你更精确的控制成员变量的处理。

如果成员变量是公共的,那么每个人都可以读写它,但是如果通过函数获取或设置它的值,就可以实现“无访问”、“只读访问”、“读写访问”等访问控制。

比如下面的代码:

class access level { private:intnoAccess;intReadOnlyintWriteOnlyintreadWritepublic: //...intgetReadOnly { returnReadOnly} VoIctSetWriteOnlY(inti){ WriteOnlY = I;} void setreadwrite(inti){ read write = I;} IntradReadWrite { returnReadWrite;} };

有必要以如此精细的方式限制每个数据成员的访问。

原因三:套餐!

这是最有说服力的理由!C++ OOP最重要的一个属性就是封装!在接口后面封装数据成员为“所有可能的实现”提供了灵活性。

包装比我们第一次看到它的时候更重要。如果我们对客户隐藏成员变量,我们可以确保类的约束得到维护,因为只有成员函数才能影响它们。Public就是没有封装,几乎可以说没有封装就是没有变化,尤其是对于广泛使用的类。被广泛使用的类是最需要打包的一个群体,因为他们可以受益于“采用一个教学实施版本来代替”。

让我们继续讨论受保护的封装。

大多数人认为受保护的比公开的更封装。其实不是。更准确的判断方法是,某物的封装与它的内容发生变化时可能造成的代码破坏量成反比。所谓的改变,可能就是把他开除出班。因此,我们可以做如下分析。至于public的成员变量,如果去掉,就意味着要销毁所有使用它的客户代码。很大的伤害,对吧?至于受保护的成员变量,如果我们去掉,就意味着销毁所有的派生类(销毁量大吗?所以受保护和公共的封装其实是一样的。这意味着一旦我们决定将一个成员变量声明为public或,就很难改变成员变量中包含的所有内容。

结论是只有两种访问权限:私有(实现封装)和其他(不实现封装)

6.将成员函数替换为非成员和非朋友

面向对象的代码要求数据和那些操作数据的函数绑定在一起,这意味着它建议所有操作数据成员的函数都应该是成员函数。但事实是这样吗?

问题产生

假设我们想写一个类来描述网页:

class { public:...void clearCache作废clearHistory无效删除;...//用户希望一个函数能够清楚地知道所有的信息。//问题是,这个函数是否应该声明为成员?void clearEverything};//也可以声明为非成员void clear everything(web browser &:){...}

那么哪个选项更好呢?

问题解决

根据面向对象代码的要求,将其声明为成员函数应该是更好的选择。但是,这是对面向对象真谛的误解。面向对象要求数据尽可能封装。然而,与直觉相反,成员函数clearEverything带来的封装比非成员函数少。此外,提供非成员函数可以为网络浏览器相关函数提供更大的包装灵活性,最终降低编译依赖性,增加网络浏览器的可导性。我们给出如下理由。

封装。封装的东西越多,能压的人越少,我们改变的灵活性就越大,我们的改变只会影响到那些看到改变的人和事。这就是我们推崇封装的原因:它使我们能够改变事物,并且只影响有限的客户。

考虑对象中的数据。代码能看到的数据越少,能封装的数据就越多,我们就越能自动改变对象数据。能访问数据成员的函数越多,数据封装越差!

所以因为非成员非友函数不能直接改变数据成员,所以可以最大限度的实现封装。

解决方案优化

在C++中,最自然的方式是让clearEverything被称为非成员函数,并位于与WebBrowser相同的函数中:

命名空间WebBrowserStuff {类WebBrowser {...};void clear everything(WebBroswer & amp;web);...}

不使用命名空间和类!重要的是,前者可以跨多个源文件,而后者不能!

像clearEverything这样的函数都是方便函数。虽然他们对网络浏览器没有特殊的访问权限,但他们可以极大地方便客户。其实我们会补充大量相似的便利函数,它们可能属于不同的模块,所以我们把不同模块的便利函数写在不同的头文件中,但是它们都属于同一个命名空:

#include "webbrowser.h "提供了类声明本身及其核心功能,命名空间web browser stuff { class web broser}。...};...class = ' class 1 ' >//头文件" web browserbokmarks . h "//与标记相关的命名空间webbrowserstuff...//与标签相关的便利函数}//头文件“browsercookies.h”命名空间素材{...//以及相关的便利功能}...

请注意,这是C++标准库的组织方式。标准库没有一个单一的、完整的和庞大的

7.如果所有参数都需要类型转换,请为此使用非成员函数

让类支持隐式类型转换通常是个坏主意。也有例外,例如,当您创建一个数字类型时。

问题产生

假设我们需要设计一个有理数类:

class Rational { public:Rational(int numerator = 0 int分母= 1);intnumeratorconstintdenominatorconst私人:...};Rational类{ public:...const Rational运算符*(const Rational & amp;RHS)const;};//所以乘法有理一八(18)很容易实现;有理one half(12);有理结果= 1/2 * 1/8;//无问题结果=结果* one Quartist;//没问题

然而,到目前为止,致命的问题还没有被认识到:

结果= one half * 2;//好!结果= 2 * 1/2;//错误!//result = 2 . operator *(oneHalf);当然错了!

由于隐式类型转换,第一个公式可以成立。编译器知道你在传递一个int,函数需要rational,但是它也知道只要你调用rational构造函数,给你提供的int,你就可以改变一个合适的Rational,所以它就这么做了。相当于:

construction(2);结果= oneHalf * temp

当然,这仅仅涉及到非显式的构造函数来做到这一点。如果是显式的,则无法编译该语句。

问题解决

结果= one half * 2;//好!结果= 2 * 1/2;//错误!

只有当参数列中列出某个参数时,该参数才是隐式类型转换的合格参与者。其地位相当于“被调用成员函数所属的对象”——即这个对象——的隐喻参数,绝不是隐含转换的合格参与者。这就是为什么语句1可以编译,而语句2不能。

因此,方法是调用operator*一个非成员函数,允许编译器对每个参数执行隐式类型转换。

const Rational运算符*(const Rational & amp;理性常数。rhs) {...} Rational one fourth(14);有理结果= one fourth * 2;//对!结果= 2 * 1/4;//对!

附加思考:

接线员*应该被宣布为朋友吗?

答案是否定的!请注意,会员的对面不是朋友,而是非会员!在这段代码中,operator*可以通过的公共接口完成任务,因此没有必要将他声明为朋友。只要能避开好友功能,就应该避开。

总结:

如果你需要输入一个函数的所有参数(包括这个),那么这个函数必须是非成员。

8.考虑如何编写专门的函数

互换作为STL的一部分,已经成为一种异常安全的脊柱,成为处理自我赋值可能性的常用机制。因为这个函数如此有用,也意味着它有着超乎寻常的复杂性。本节讨论这些复杂性以及如何相应地处理它们。

问题产生1

namespacestd { template & lttypenameT>。空隙交换(T & amp美国电话电报公司;b){ T temp(a);a = b;b =;} }

这是标准库提供的交换。很简单,只要t有复制相关操作。然而,这种算法在某些情况下效率不高。比如在处理“用真实数据指向一个对象”这种类型时。(这种设计的常见形式是所谓的“皮条客技术:实现的指针”。)

类Impl {//实现细节不重要。为小部件设计的公共://类...Private: int a,b,c;STD::vector & lt;double>。v;...};类Widget { public:Widget(const Widget & amp;RHS);Widget&。=(const Widget & amp;rhs) {...* PImpL = *(RHS . PImpL);...} private:Widgetimpl * PimpL;};

调用库的交换是非常低效的。因为他总共要复制三个Widget和三个WidgetImpl对象!其实只需要改变指针的指向即可。

问题解决1

我们可以尝试用以下方法解决这个问题,让swap专门化Widget。

试试一个:

namespacestd { template & lt>。//表示他是完全专业的虚空互换

一般来说,我们不能改变std命名空中的任何内容,但是我们可以(允许)为标准模板制作专门的版本。

但其实这个是编不出来的。因为他试图给班上的私人成员打电话。

所以他调用成员函数更合理。

解决方案:

类别小部件{公共:...无效交换(窗口小部件& amp其他){使用STD::swap;swap(pImpl,other . Pinmpl);}...};private:Widgetimpl * PimpL;};命名空间标准{ template & lt>。作废掉期<。Widget>。(Widget & amp一、Widget & ampb){ a . swap(b);} }

该方法不仅可以编译,而且与STL容器一致。

问题生成2

假设WidgetImpl和WidgetImpl都是类模板而不是类,也许我们可以尝试在Widgetimpl中参数化数据类型:

模板<。typenameT>。类别WidgetImpl {...};模板<。typenameT>。类别Widget {...};//在Widget中放交换成员函数一如既往的简单。//但是写专门的std::swap的时候有一个问题。namespacestd {模板

以上专业调剂其实是有问题的。我们尝试专门化这个函数模板,但是C++只允许类模板的专门化。(后面会介绍完全专门化和部分专门化)。当您试图专门化一个函数模板时,更常见的是添加重载函数:

namespacestd { template & lttypenameT>。void swap(Widget & lt;T>。& amp一、小工具& ltT>。& ampb){ a . swap(b);} }

但其实这是不行的!因为std是一个特殊的名称空,并且它的管理规则是特殊的,所以客户可以在std中完全专门化模板,但是他们不能在std中添加新模板。

问题解决2

解决这个问题的方法是声明一个非成员交换,让它调用成员交换,但不要声明该非成员交换为std::swap专门化版本或重载版本。

命名空间WidgetStuff { template & lttypenameT>。类别WidgetImpl {...};模板<。typenameT>。类别Widget {...};...模板<。typenameT>。无效交换(Widget & ltT>。& amp一、小工具& ltT>。& ampb){ a . swap(b);} }

现在如果你打算随时替换两个Widget对象,调用swap,C++的名字搜索算法会在WidgetStuff中找到Widget独占版本。

这种方法适用于类和类模板。如果您希望您的“类”独占交换在尽可能多的上下文中被调用,您需要在类的名称空中编写一个非成员版本和一个std::swap专用版本。

另外,如果一个名为空的房间没有像上面那样使用,那么上面的东西仍然会被使用。但是为什么要把那么多东西放在全局命名室空?

补充思维

目前提到的都与互换写作有关。现在让我们换个角度思考,从客户的角度来看问题。假设我们需要编写一个函数模板:

模板<。typenameT>。无效剂量(T & ampobj1,T & ampobj2) {...swap(obj1,obj 2);...}

这个时候调用哪个版本的swap?当然,我们希望调用T的独占版本,如果那个版本不存在,就调用std中的一般化版本。

模板<。typenameT>。无效剂量(T & ampobj1,T & ampobj2) {使用STD::swap;...swap(obj1,obj 2);//调用t型对象的最佳交换版本。...}

名称查找算法可确保在全局范围内或t所在的名称空内找到任何特定于t的交换。如果t是一个WidgetStuff,并且位于名为空之间的WidgetsTuff中,编译器将使用“取决于参数的搜索规则”来找出Stuff中的交换。如果没有特定于t的交换,编译器将在std中使用交换。

以下是我设计的一个不太符合逻辑的代码,但是证明了上面的说法是合理的。

#include <。iostream>。使用命名空间TD;test { class try { public:void swap(try & amp;第一,幽会。二){ cout & lt& lt“是的!”& lt& ltendl} };空隙率<。& lt“是的!”& lt& ltendl} }intmain(intargc,char* argv) {//在此插入代码...test::try a;intb = 12{ using STD::swap;swap(b,b);swap(a,a);} return0}/*是!程序以退出代码结束:0 */

总结:

如果swap的默认实现版本效率低下(这几乎意味着您的类或模板使用了某种皮条客技术),您可以尝试做以下事情:

提供一个公共交换成员函数来有效地替换您的类型的两个对象值。

在您的类或模板所在的命名空房间中,提供一个非成员交换,并命令它调用上述交换成员函数。

如果您正在编写一个类,请为您的类专门化::swap,并让它调用您的swap成员函数。

最后,如果您调用,请确保包含一个using声明。

为什么模板要专门化,因为人们认为,如果你能针对特定类型更好地实现某个功能,你就应该听你的。

模板分为类模板和函数模板,而特殊化分为完全特殊化和部分特殊化。完全特殊化是定义死模板的具体类型,而部分特殊化是在有多种类型模板的情况下只定义其中的一部分。

先看一下班级模板:

模板<。类型名T1,类型名T2 & gt;class Test {public:Test(T1 i,T2 j):a(i),b(j){ cout & lt;& lt"模板类"

然后下面三句话依次叫类模板、全特殊化、偏特殊化:

测试<。doubledouble>。t1(0 . 10 . 2);测试<。intchar>。T2(1 ' A ');测试<。charbool>。T3(' A ' true);

至于函数模板,只是完全专门化,而不是部分专门化:

//模板函数模板

至于为什么函数不能特殊化,似乎不是因为语言不能实现,而是因为特殊化的功能可以通过重载函数来完成。

(摘自模板的完全专门化和部分专门化)

本文转自:CSDN博客

微信号:概念

1.《接口设计 关于接口的设计与声明--对封装性的理解》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《接口设计 关于接口的设计与声明--对封装性的理解》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/yule/801119.html

上一篇

曼城重回榜首 积分英超:九队仍可争冠,阿森纳重回上半区,曼城后来居上!

下一篇

彩臂金龟 男子放生彩臂金龟原因是什么 彩臂金龟价格值多少钱?

水木年华成员李健 水木年华歌手李健自爆退队原因 其老婆照片曝光

水木年华成员李健 水木年华歌手李健自爆退队原因 其老婆照片曝光

  近期,水木年华原成员李健因加盟《中国正在听》节目而再次因因为关注,同时,歌手李健个人资料及老婆照片等被曝光,李健还曾自爆退出水木年华原因。下面红圈星闻带来相关信息。  歌手李健  李健,中国流行男歌手,黑龙江哈尔滨人。...

全民公决 【英国脱欧】N. 福斯特:英国关于欧盟成员资格的第二次全民公决

  • 全民公决 【英国脱欧】N. 福斯特:英国关于欧盟成员资格的第二次全民公决
  • 全民公决 【英国脱欧】N. 福斯特:英国关于欧盟成员资格的第二次全民公决
  • 全民公决 【英国脱欧】N. 福斯特:英国关于欧盟成员资格的第二次全民公决

板野友美下海 AKB48成员下海拍片 片酬丰厚难敌诱惑

AKB48成员小野惠令奈下海拍片,很难动心AKB48成员小野惠令奈下海拍片,很难动心Star.com 7月30日报道,原AKB48成员中西里菜、成田梨纱、大江朝美、乌萨马·姬友、高松·会理、板野友美等前辈下海拍片后,另一名AKB48成员退出,接受7亿日元的邀请,拍成...

akb48下海 AKB48成员下海拍片 片酬丰厚难敌诱惑

AKB48成员小野惠令奈下海拍片,很难被诱惑AKB48成员小野惠令奈下海拍片,很难被诱惑Star.com 7月30日报道,原AKB48成员中西里菜、成田梨纱、大江朝美、乌萨马·姬友、高松·会理、板野友美等前辈下海拍片后,另一名AKB48成员退出,接受7亿日元的邀请,...

雇佣兵公司 现役4大雇佣兵团:第1成员达到100万,第2专门猎杀特种兵

  • 雇佣兵公司 现役4大雇佣兵团:第1成员达到100万,第2专门猎杀特种兵
  • 雇佣兵公司 现役4大雇佣兵团:第1成员达到100万,第2专门猎杀特种兵
  • 雇佣兵公司 现役4大雇佣兵团:第1成员达到100万,第2专门猎杀特种兵
防弹少年团成员介绍 防弹少年团成员资料 防弹少年团在韩国火吗

防弹少年团成员介绍 防弹少年团成员资料 防弹少年团在韩国火吗

  防弹少年团成员资料 防弹少年团在韩国火吗  防弹少年团(BTS),韩国BigHit Entertainment旗下2013年出道的男子演唱组合,由金南俊(Rap Monster)、金硕珍(JIN)、闵玧其(SUGA)、...

林超贤行动三部曲 红海行动有没有第二部 林超贤下一部同类型作品是什么

  • 林超贤行动三部曲 红海行动有没有第二部 林超贤下一部同类型作品是什么
  • 林超贤行动三部曲 红海行动有没有第二部 林超贤下一部同类型作品是什么
  • 林超贤行动三部曲 红海行动有没有第二部 林超贤下一部同类型作品是什么

excel乘积函数 [Excel]忘记乘积求和,SUMPRODUCT其实还能这么用……

  • excel乘积函数 [Excel]忘记乘积求和,SUMPRODUCT其实还能这么用……
  • excel乘积函数 [Excel]忘记乘积求和,SUMPRODUCT其实还能这么用……
  • excel乘积函数 [Excel]忘记乘积求和,SUMPRODUCT其实还能这么用……