2007年12月7日

  哈哈,从M$ Visual C++ Team的Andy Rich那里又偷学到一招:VC8的隐含编译项/d1reportSingleClassLayout/d1reportAllClassLayout 。看个复杂的例子吧(如下),现在假设我们想知道Derived类的对象布局,怎么办? 在Project Properties->C++->Command Line->Additional Options里面加上/d1reportSingleClassLayoutDerived吧!

class CommonBase
{
    
int co;
};

class Base1: virtual public CommonBase
{
public:
    
virtual void print1() {}
    
virtual void print2() {}
private:
    
int b1;
};

class Base2: virtual public CommonBase
{
public:
    
virtual void dump1() {}
    
virtual void dump2() {}
private:
    
int b2;
};

class Derived: public Base1, public Base2
{
public:
    
void print2() {}
    
void dump2() {}
private:
    
int d;
};

int _tmain(int argc, _TCHAR* argv[])
{
    
return 0;
}

F5编译之,你会惊奇地发现,Output里面有如下字样:

 1 class Derived size(32):
 2    +---
 3    | +--- (base class Base1)
 4  0 | | {vfptr}
 5  4 | | {vbptr}
 6  8 | | b1
 7    | +---
 8    | +--- (base class Base2)
 9 12 | | {vfptr}
10 16 | | {vbptr}
11 20 | | b2
12    | +---
13 24 | d
14    +---
15    +--- (virtual base CommonBase)
16 28 | co
17    +---
18 
19 Derived::$vftable@Base1@:
20  0 | &Base1::print1
21  1 | &Derived::print2
22 
23 Derived::$vftable@Base2@:
24  0 | &Base2::dump1
25  1 | &Derived::dump2
26 
27 Derived::$vbtable@Base1@:
28  0 | -4
29  1 | 24 (Derivedd(Base1+4)CommonBase)
30 
31 Derived::$vbtable@Base2@:
32  0 | -4
33  1 | 12 (Derivedd(Base2+4)CommonBase)
34 
35 Derived::print2 this adjustor: 0
36 Derived::dump2 this adjustor: 12

看到了吗? VC8居然输出了Derived对象的完整布局! 我们终于可以不必两眼一抹黑般的去peek/poke了....第1行表明,Derived对象总占用了32字节;其由三部分组成,分别是行3-行7、行 8-行12、行13、行28;其中前二者分别是基类Base1、Base2的布局,最后的行28为虚拟基类Common的布局。

以基类 Base1部分为例,可发现其由一个虚函数表指针vftable和虚基表指针vbtable构成,先看Base1部分的vftable所指向的虚表$ vftable@Base1(行19),不难发现,其中的表项2已经被Derived::print2给override了;再来看Base2部分的 vftable所指向的虚表$vftable@Base2(行23),可发现,同样的,Base2::dump2被Derived::dump2给 override了。这不明摆着就是虚函数机制嘛,heh~

值得注意的是,这个例子同时说明,多继承场合下,其实在单一对象中是存在多个 this指针的....行35-36给出了如何将Derived的this指针校正为其基类子对象this指针的偏移量,也就是说,根据行36,假设有个 Derived d,那么d.dump1()实际上应该理解成通过虚表$vftable@Base2对((Base2*)(((char*)&d)+12))- >dump1()的调用....即传递给所有Base2成员函数的this指针应该是(Base2*)((char*)(&d)+12),这里可能我写得恐怖了点,意思到了就成....这不,普通继承、多继承、对象Slicing的语义都在这个布局里面了,看仔细了哈~

OK,多继承看完了,继续看虚拟基类是如何布局的。虚基Common在Derived的布局中,位于Derived本身数据成员之后的位置。Base1、 Base2中均保存了一个vbtable指针,其分别指向各自所使用的虚基表$vbtable@Base1和$vbtable@Base2,为什么要指向一个虚基表? 很简单,因为Base1、Base2有可能会同时继承多个不同的虚拟基类.....这充分体现了C++对象布局的复杂性....在每个虚基表中,保存了所继承的虚拟基类部分相对于子类部分vbtable指针的偏移值,以Base2为例,我们知道Base2的vbtable在Derived中的偏移值为16 (行10),则根据$vbtable@Base2,虚基Common部分距离Base2 vbtable指针的偏移值为12,则有虚基Common在Derived中的总偏移值为16+12。与普通多继承同理,我们在调用非虚拟的虚基成员函数时,必须将Derived的this指针调整为指向虚基部分的this指针,只有这样才能成功地访问虚基自身的数据成员和虚基的虚拟函数(通过虚基自己的 vftable,为简单起见,上例中我就没弄那么复杂了,大家可以自己玩玩,明白如何举一反三即可)
posted @ 2007-12-07 09:56 daiyie 阅读(635) | 评论 (2)编辑 收藏

2007年10月25日

Transferring the Password

There are various ways to get the passwords stored in KeePass into other windows. The first, and most simple method is copying them to the clipboard. For this, just double-click onto the specific field in the main password list. Example: if you want to copy the password of entry X, point onto the password field of the entry in the main view and double-click. The password is copied to the clipboard. If you enable the auto-clearing option, KeePass will clear the clipboard automatically after some seconds. This prevents you from forgetting to clear the clipboard yourself, leaving sensitive data in the clipboard.

The second method is drag-n-drop. As in method 1, point onto the field you want to use, click the left mouse button and hold it. Drag the data into other windows.

The third, and the most powerful method is auto-type. KeePass features a very mighty auto-type feature, which types user names, passwords, etc. into other windows for you. The default auto-typing sequence is: {USERNAME}{TAB}{PASSWORD}{TAB}{ENTER}. But this sequence is customizable, per entry (read the CHM documentation file that comes with KeePass for more about this). This makes the auto-type feature applicable to all windows and webforms you'll ever see. There are two submethods to perform an auto-type:

  • Selecting an entry: Just select the entry that you want to get auto-typed, right-click onto it and click "Perform Auto-Type". KeePass will minimize itself, the window that had the focus before will come to the front. KeePass starts typing the data into this window.
  • Global hot key: This is the most powerful of all methods. You leave KeePass running in the background. As soon as you're on a site that requires a login (the password of which you stored in KeePass before), just press a hot key (by default, Ctrl-Alt-A). KeePass immediately auto-types the data into the target window.
posted @ 2007-10-25 21:31 daiyie 阅读(393) | 评论 (0)编辑 收藏

2007年9月23日

#include <boost/config.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <vector>
#include <algorithm>

template<class T>
inline T & ToRef( T *pT)
{
 return *pT;
}
#define TODEF(x) bind( ToRef<x>, _1)

template<class T>
inline void DeletePointer( T *pT)
{
 delete pT;
}
#define ALL(v) (v).begin(), (v).end()

struct X
{
public:
 int m_t;
 X():m_t(11)
 {
 }
 
    bool f(int a)
 {
  std::cout << m_t << std::endl;
  return true;
 }
};

int main()
{
    using namespace boost;
 using namespace std;
 
 vector<X *> v;
 for( int i=0; i<10; ++i)
  v.push_back( new X);
 
 for_each( ALL(v), bind(&X::f, TODEF(X), 4 ) );
 
 for_each( ALL(v), bind( DeletePointer<X>, _1) );
 
 return 0;
}

 

posted @ 2007-09-23 17:09 daiyie 阅读(217) | 评论 (1)编辑 收藏

2007年5月23日

一天,一只兔子在山洞前写论文。
一只狼过来,问兔子:‘你在写什么?” 
答:“论文”
狼问:“你的论文的主题是什么?” 
答:“《论兔子如何吃掉狼》”。
狼听了哈哈大笑。
兔子说,我写的论文大部分稿子在洞里,我把道理写的很清楚。
狼想看看兔子的论文是怎么写的。于是兔子把狼领进山洞。
过了一会,兔子独自走出山洞。

兔子继续在山洞前写它的论文。
一只狐狸过来,问:“你在写什么?”
答:“我在写论文”。
“论文的主题是什么?” 
答:“论兔子如何吃掉狐狸”。 
向来狡猾的狐狸也笑了。说:“这怎么可能呢”
兔子说:“我写的大部分稿子还在洞里,我把道理写的很清楚。”
狐狸想去看看兔子的论文是怎么写的,于是兔子把狐狸领进山洞。
过了一会儿,兔子独自一个走出山洞。

最后,在山洞里一只狮子在几堆白骨之间,满意地一边剔着牙,一边阅读兔子交给它的论文的提要:“一个动物,能力大小并不重要,关键看你的导师是谁。
posted @ 2007-05-23 16:27 daiyie 阅读(327) | 评论 (1)编辑 收藏

据说这是道中国人老是选错的题,当然这样说是有所隐喻的,不过我们大可以跳出政治的范畴,实际点说,对于政治,我们大多数人是心有余而力不足的,于是会有各种表现,焦虑的就成了愤青一族,忍受的就成了洗脑一族,总之都没什么好词儿,还没怎么呢就先自己掐起来了......

 

上帝把两群羊放在草原上,一群在南,一群在北。

上帝还给羊群找了两种天敌,一种是狮子,一种是狼。

上帝对羊群说:“如果你们要狼,就给一只,任它随意咬你们。

如果你们要狮子,就给两头,你们可以在两头狮子中任选一头,还可以随时更换。”

 

这道题的问题就是:如果你也在羊群中,你是选狼还是选狮子?

很容易做出选择吧?

好吧,记住你的选择,接著往下看。

 

南边那群羊想,狮子比狼凶猛得多,还是要狼吧。于是,它们就要了一只狼。

北边那群羊想,狮子虽然比狼凶猛得多,但我们有选择权,还是要狮子吧。于是,它们就要了两头狮子。

 

那只狼进了南边的羊群后,就开始吃羊。

狼身体小,食量也小,一只羊够它吃几天了。

这样羊群几天才被追杀一次。

 

北边那群羊挑选了一头狮子,另一头则留在上帝那里。

这头狮子进入羊群后,也开始吃羊。狮子不但比狼凶猛,而且食量惊人,每天都要吃一只羊。

这样羊群就天天都要被追杀,惊恐万状。

 

羊群赶紧请上帝换一头狮子。不料,上帝保管的那头狮子一直没有吃东西,正饥饿难耐,它扑进羊群,比前面那头狮子咬得更疯狂。

羊群一天到晚只是逃命,连草都快吃不成了。

 

南边的羊群庆幸自己选对了天敌,又嘲笑北边的羊群没有眼光。

北边的羊群非常后悔,向上帝大倒苦水,要求更换天敌,改要一只狼。

上帝说:“天敌一旦确定,就不能更改,必须世代相随,你们唯一的权利是在两头狮子中选择。”

 

北边的羊群只好把两头狮子不断更换。

可两头狮子同样凶残,换哪一头都比南边的羊群悲惨得多,它们索性不换了,让一头狮子吃得膘肥体壮,另一头狮子则饿得精瘦。

眼看那头瘦狮子快要饿死了,羊群才请上帝换一头。

 

这头瘦狮子经过长时间的饥饿后,慢慢悟出了一个道理:

自己虽然凶猛异常,一百只羊都不是对手,可是自己的命运是操纵在羊群手里的。

羊群随时可以把自己送回上帝那里,让自己饱受饥饿的煎熬,甚至有可能饿死。

想通这个道理后,瘦狮子就对羊群特别客气,只吃死羊和病羊,凡是健康的羊它都不吃了。

羊群喜出望外,有几只小羊提议干脆固定要瘦狮子,不要那头肥狮子了。

一只老公羊提醒说:“瘦狮子是怕我们送它回上帝那里挨饿,才对我们这么好。

万一肥狮子饿死了,我们没有了选择的余地,瘦狮子很快就会恢复凶残的本性。”

羊群觉得老羊说得有理,为了不让另一头狮子饿死,它们赶紧把它换回来。

 

原先膘肥体壮的那头狮子,已经饿得只剩下皮包骨头了,并且也懂得了自己的命运是操纵在羊群手里的道理。

为了能在草原上待久一点,它竟百般讨好起羊群来。而那头被送交给上帝的狮子,则难过得流下了眼泪。

北边的羊群在经历了重重磨难后,终于过上了自由自在的生活。

 

南边的那群羊的处境却越来越悲惨了,那只狼因为没有竞争对手,羊群又无法更换它,

它就胡作非为,每天都要咬死几十只羊,这只狼早已不吃羊肉了,它只喝羊心里的血。

它还不准羊叫,哪只叫就立刻咬死哪只。

南边的羊群只能在心中哀叹:“早知道这样,还不如要两头狮子。”

posted @ 2007-05-23 16:25 daiyie 阅读(340) | 评论 (1)编辑 收藏

2007年5月15日

 

开始之前,确认:
1.安装完成Visual Studio 2005,确认安装的时候选中了Unicode MFC version以及CRT source code(默认是选中的,如果你没有改动的话那么应该是装上了的);
2.找到我们要动手术的目录,在$(Program Files)\Microsoft Visual Studio 8\VC 下,找出ATLMFC\SRC和CRT\SRC两个文件夹;
3.(可选)安装Platform SDK,复制最新版本的unicows.lib放到VC\PlatformSDK\LIB中。VC8自带有unicows.lib,所以这一步可以不做。
一:编译MSLU的CRT库8.0
首先请备份一下VC\Lib、VC\CRT\SRC、VC\ATLMFC\SRC、VC\ATLMFC\lib这四个文件夹(包括子文件夹),假如不小心改错的话可以直接从备份的文件夹里复制文件出来。
CRT库需要编译的是这6个DLL:MSVCR80.DLL、MSVCP80.DLL、MSVCM80.DLL、MSVCR80D.DLL、MSVCP80D.DLL、MSVCM80D.DLL。为方便操作,我们把将要输出的MSLU的CRT库文件改为别的名字以免和原来的混淆,分别改成:MSLUR80.DLL、MSLUR80D.DLL、MSLUP80.DLL、MSLUP80D.DLL、MSLUM80.DLL、MSLUM80D.DLL(如果你改成别的名字的话,后面的操作请做相应的改正)。
(1)先把SRC目录下的相应文件改名:
copy _SAMPLE_.RC MSLUR80.RC
copy SAMPLE_P.RC MSLUP80.RC
copy SAMPLE_M.RC MSLUM80.RC
copy SAMPLE_P.DEF MSLUP80.DEF
copy SAMPLD_P.DEF MSLUP80D.DEF
copy SAMPLE_M.DEF MSLUM80.DEF
copy SAMPLD_M.DEF MSLUM80D.DEF
copy SAMPLE_U.DEF MSLUU80.DEF
copy SAMPLD_U.DEF MSLUU80D.DEF
copy Intel\_SAMPLE_.DEF Intel\MSLUR80.DEF
copy Intel\_SAMPLD_.DEF Intel\MSLUR80D.DEF
(2)打开MSLUP80.DEF,修改LIBRARY SAMPLE_P为LIBRARY MSLUP80;
打开MSLUP80D.DEF,修改LIBRARY SAMPLD_P为LIBRARY MSLUP80D;
打开MSLUM80.DEF,修改LIBRARY SAMPLE_M为LIBRARY MSLUM80;
打开MSLUM80D.DEF,修改LIBRARY SAMPLD_M为LIBRARY MSLUM80D;
打开MSLUU80.DEF,修改LIBRARY SAMPLE_M为LIBRARY MSLUM80;(注意此处不是MSLUU80而是MSLUM80)
打开MSLUU80D.DEF,修改LIBRARY SAMPLD_M为LIBRARY MSLUM80D;(注意此处不是MSLUU80D而是MSLUM80D)
打开\Intel\MSLUR80.DEF,修改LIBRARY _SAMPLE_为LIBRARY MSLUR80;
打开\Intel\MSLUR80D.DEF,修改LIBRARY _SAMPLD_为LIBRARY MSLUR80D;
(3)打开SRC\makefile文件,修改开头部分为:
RETAIL_DLL_NAME=MSLUR80
RETAIL_DLL_NAME=MSLUR80
RETAIL_LIB_NAME=MSLUR80
RETAIL_DLLCPP_NAME=MSLUP80
RETAIL_LIBCPP_NAME=MSLUP80
RETAIL_DLLMIXED_NAME=MSLUM80
RETAIL_LIBMIXED_NAME=MSLUM80
RETAIL_LIBPURE_NAME=MSLUU80
RETAIL_PT_LIBMIXED_NAME=MLSUPTM
RETAIL_PT_LIBPURE_NAME=MLSUPTU
DEBUG_DLL_NAME=MSLUR80D
DEBUG_LIB_NAME=MSLUR80D
DEBUG_DLLCPP_NAME=MSLUP80D
DEBUG_LIBCPP_NAME=MSLUP80D
DEBUG_DLLMIXED_NAME=MSLUM80D
DEBUG_LIBMIXED_NAME=MSLUM80D
DEBUG_LIBPURE_NAME=MSLUU80D
DEBUG_PT_LIBMIXED_NAME=MLSUPTMD
DEBUG_PT_LIBPURE_NAME=MLSUPTUD
RC_NAME=MSLUR80
RCCPP_NAME=MSLUP80
RCMIXED_NAME=MSLUM80
54行处修改VCTOOLS,注意自己的VS2005安装目录,假如安装到E:\SoftWare\Microsoft Visual Studio 8\下,则此处改为:
VCTOOLS=E:\SoftWare\Microsoft Visual Studio 8\VC
1779, 1841, 1905, 1940, 2037, 2105, 2139, 2230行的kernel32.lib,改为:unicows.lib kernel32.lib(一共8个不要改多了);
(4)打开Visual Studio 2005 command prompt(从开始菜单或者是vc8的开发环境的菜单都可以启动),进入到SRC目录,输入命令设置vctools,假如VS2005安装在E:\SoftWare\Microsoft Visual Studio 8\下,则输入:
Set vctools=E:\SoftWare\Microsoft Visual Studio 8\VC
完成后输入bldnt启动SRC目录下的bldnt.bat批处理文件编译CRT库。
译者:我编译的时候出来好多warning,不管他最后还是编译完成了。
(5)编译完成之后lib文件都放在SRC\BUILD\INTEL\目录下,把文件名字改回来以便以前的程序可以链接,而且下面编译MFC库也需要。DLL文件不用改,新的lib文件知道该链接到谁那里。改名:
copy MLSUPTM.LIB PTRUSTM.LIB
copy MLSUPTMD.LIB PTRUSTMD.LIB
copy MLSUPTU.LIB PTRUSTU.LIB
copy MLSUPTUD.LIB PTRUSTUD.LIB
copy MSLUR80.LIB MSVCRT.LIB
copy MSLUR80D.LIB MSVCRTD.LIB
copy MSLUP80.LIB MSVCPRT.LIB
copy MSLUP80D.LIB MSVCPRTD.LIB
copy MSLUM80.LIB MSVCMRT.LIB
copy MSLUM80D.LIB MSVCMRTD.LIB
copy MSLUU80.LIB MSVCURT.LIB
copy MSLUU80D.LIB MSVCURTD.LIB
(6)把改名后的lib文件复制到VC\LIB目录下覆盖原来的文件。
(7)把步骤(4)、(5)、(6)再执行一次来重建MSLUP80(D).DLL和静态库,以便他们能链接到我们自己的MSVCRT(D).LIB(指向我们的MSLUR80(D).DLL)。
关掉VS2005 command prompt,因为编译CRT库的时候定义了许多环境变量,这些会使后面MFC库的编译出现错误。
6个新的DLL文件都在\VC\crt\src\build\intel下,把这6个文件复制到系统目录下。
二:编译MSLU的MFC库8.0 Unicode版本
MFC库需要编译的是这4个DLL:
MFC80U.DLL (Unicode Release)
MFC80UD.DLL (Unicode Debug)
MFCM80U.DLL (Mixed mode/Managed Unicode Release)
MFCM80UD.DLL (Mixed mode/Managed Unicode Debug)。
为方便操作,我们把将要输出的MSLU的CRT库文件改为别的名字以免和原来的混淆,分别改成:
MFC80LU.DLL
MFC80LUD.DLL
MFCM80LU.DLL
MFCM80LUD.DLL
(如果你改成别的名字的话,后面的操作请做相应的改正)。
(1)打开VC\ATLMFC\SRC\MFC\MFCDLL.MAK,在274、307行的link @<<,后面插入一行变成:
 link @<<
/nod:kernel32.lib /nod:advapi32.lib /nod:user32.lib /nod:gdi32.lib /nod:shell32.lib /nod:comdlg32.lib /nod:version.lib /nod:mpr.lib /nod:rasapi32.lib /nod:winmm.lib /nod:winspool.lib /nod:vfw32.lib /nod:secur32.lib /nod:oleacc.lib /nod:oledlg.lib /nod:sensapi.lib
180行的
LIBS=$(CRTDLL) kernel32.lib gdi32.lib msimg32.lib user32.lib uuid.lib daouuid.lib htmlhelp.lib shlwapi.lib $(PROFLIB)
改为:
LIBS=$(CRTDLL) unicows.lib kernel32.lib advapi32.lib comdlg32.lib shell32.lib oledlg.lib winspool.lib oleacc.lib gdi32.lib msimg32.lib user32.lib uuid.lib daouuid.lib htmlhelp.lib shlwapi.lib $(PROFLIB)
(此处不同于原文。原文为:
LIBS=$(CRTDLL) unicows.lib kernel32.lib gdi32.lib msimg32.lib user32.lib uuid.lib daouuid.lib htmlhelp.lib shlwapi.lib $(PROFLIB)
译者依照原文修改,编译的时候出现了错误,增加数个lib文件后编译通过)
(2)打开VC\ATLMFC\SRC\MFCM\MFCMDLL.MAK,在253、272行的link @<<,后面插入一行变成:
 link @<<
/nod:kernel32.lib /nod:advapi32.lib /nod:user32.lib /nod:gdi32.lib /nod:shell32.lib /nod:comdlg32.lib /nod:version.lib /nod:mpr.lib /nod:rasapi32.lib /nod:winmm.lib /nod:winspool.lib /nod:vfw32.lib /nod:secur32.lib /nod:oleacc.lib /nod:oledlg.lib /nod:sensapi.lib
215行的
LIBS=$(CRTDLL) $(CRTMDLL) mscoree.lib kernel32.lib gdi32.lib msimg32.lib user32.lib \
改为:
LIBS=$(CRTDLL) $(CRTMDLL) mscoree.lib unicows.lib kernel32.lib advapi32.lib comdlg32.lib shell32.lib oledlg.lib winspool.lib mpr.lib oleacc.lib gdi32.lib msimg32.lib user32.lib \
(此处原文为:
LIBS=$(CRTDLL) $(CRTMDLL) mscoree.lib unicows.lib kernel32.lib gdi32.lib msimg32.lib user32.lib \
其余说明同上)
(3)打开\VC\atlmfc\src\mfcm\INTEL\mfcm80u.def 文件,把第10行的LIBRARY mfcm80u 改为:
LIBRARY mfcm80Lu
打开\VC\atlmfc\src\mfcm\INTEL\mfcm80ud.def文件,把第10行的LIBRARY mfcm80ud改为:
LIBRARY mfcm80Lud
(4)打开\VC\atlmfc\src\mfcm\wfrmcmd.cpp文件,251行的#error Following information required to build private version用//注释掉或者直接把该行删掉;253行的[assembly: AssemblyTitle("")];改为:
[assembly: AssemblyTitle("mfcm80L")];
(5)打开\VC\atlmfc\src\mfcm\interfaces\AssemblyInfo.cpp文件,78行的#error Following information required to build private version用//注释掉或者直接把该行删掉;85行的[assembly: AssemblyTitle("")];改为:
[assembly: AssemblyTitle("mfcm80Lifaces")];
(6)在\VC\atlmfc\src\下建立一个批处理文件:buildmfc.bat,内容为:
nmake -f atlmfc.mak MFC libname=MFC80L
(7)打开Visual Studio 2005 command prompt,进入目录\VC\atlmfc\src\,输入buildmfc执行buildmfc.bat编译MFC库。
编译完成之后,在\VC\atlmfc\src\mfc\intel下有MFC80LU.DLL、MFC80LUD.DLL,在\VC\atlmfc\src\mfcm\INTEL下有MFCM80LU.DLL、MFCM80LUD.DLL,把这4个DLL文件复制到系统目录下;在\VC\atlmfc\lib\INTEL下有MFC80LU.LIB、MFC80LUD.LIB、MFCM80LU.lib、MFCM80LUD.lib,把文件名改为:
MFC80U.LIB、MFC80UD.LIB、MFCM80U.lib、MFCM80UD.lib
把这四个lib文件复制到\VC\atlmfc\lib下覆盖原来的文件就完成了。
posted @ 2007-05-15 22:47 daiyie 阅读(1336) | 评论 (0)编辑 收藏
仅列出标题  

导航

<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

统计

常用链接

留言簿(1)

随笔档案

收藏夹

搜索

最新评论

阅读排行榜

评论排行榜