全新计算机书籍转让
联系人:赵小姐, 联系电话:
白天: 025-83909202(小灵通)
晚上: 025-83408282
Email:xiaomeng_zhao#yahoo.com.cn
(注意将上面Email的将#换成@噢!)
转让如下(南京以外的城市可能要邮购了):
最新消息 - 新增3本超值特价书
--------------------------
1. 人邮出版的"Visual C++ 网络开发技术"
2. 清华出版的"Windows 2000/XP中文版注册表使用开发与案例"
3. 中国水利水电出版社的"ATM网络技术"
--------------------------
英文书名: Sams Teach Yourself C# in 21 Days
中文书名: 21天学通C#
作者: Bradley L. Jones
授权出版社: Sams
被授权出版社: 人民邮电出版社
成色: 全新
原价: 60元
折扣率: 同为读书人, 您看打几折合适吧
现价: 您看着给吧:)
英文书名: Programming Microsoft Windows with Microsoft Visual Basic .Net
中文书名: Microsoft Windows程序设计 - Visual Basic .NET语言描述
作者: Charles Petzold (由 章立民 翻译)
授权出版社: Microsoft
被授权出版社: 华中科技大学出版社
成色: 全新
原价: 118元
折扣率: 同为读书人, 您看打几折合适吧
现价: 您看着给吧:)
书名: 精通Visual Basic .NET中文版
作者: 刘炳文教授(谭浩强工作室)
出版社: 机械工业出版社
成色: 全新
原价: 66元
折扣率: 同为读书人, 您看打几折合适吧
现价: 您看着给吧:)
书名: PowerBuilder应用与开发
出版社: 机械工业出版社
成色: 全新
原价: 49元
折扣率: 同为读书人, 您看打几折合适吧
现价: 您看着给吧:)
书名: 中文Visual Basic高级编程
出版社: 清华大学出版社
成色: 几乎全新
原价: 29.8
折扣率: 同为读书人, 您看打几折合适吧
现价: 您看着给吧:)
书名: Visual Basic实用技术指南
出版社: 人民邮电出版社
成色: 9成新
原价: 38元
折扣率: 同为读书人, 您看打几折合适吧
现价: 您看着给吧:)
posted @
2006-12-12 01:43 Jerry Cat 阅读(457) |
评论 (1) |
编辑 收藏
长期不变的FTP账号
极品匿名ftp
ftp://219.157.126.10
影视FTP(太原理工大学终极网域FTP)
ftp://finalmov:finalmov@202.207.240.119
ftp://sangel.xicp.net
荥经电信(综合,速度快)
ftp://yjdx:yjdx@202.98.141.74
极品匿名ftp(电影娱乐类,内容丰富)
ftp://219.157.126.10
综合FTP
ftp://www.mx3x.com
:< wind_code_1 >
高速ftp,不要密码,东西暴多!!!
ftp://221.224.20.206
武汉安全网ftp
ftp://315safe:315safe@ftp.315safe.com
KingSExftp 带宽:网通100m
ftp://bbs.winzheng.com:loveyou@218.56.97.189
影视连续剧音乐FTP:
QUOTE:
ftp://221.224.20.206
ftp://202.198.35.153
超好的音乐FTP:
ftp://219.153.2.114/
非匿名 石狮影视的临时帐号:
ftp://石狮影视免费下载:bbs.yssn.com石狮影视@ftp1.ssdvd.com:21
蓝魂主力FTP:
ftp.bluesma.com:3131
帐号:蓝魂主力服务器
密码:蓝魂论坛精彩无限
http://www.bluesma.com[/url
]
匿名电影ftp
202.102.234.88
兰荫补档区主力1号
ftp://ly_budang:reqisthebest123@ftp1.lanyin.net/
天香APE音乐
ftp://rt_admin:3166admin@hclhcl7.vicp.net:3000
烟雨江南影视FTP
ftp://烟雨江南:bbs.tc.cn@ftp.wolf2000.com/
ftp站点(dvd)
ftp://dvd:dvd@210.34.14.166:2121
ftp://ydy.com.high:qSDMPJASo7XTBz@ftp.tzssyxx.com/
ftp://shiyushen.vicp.net
用户名:down
密码:fox**fox**down
学校教育的FTP:
QUOTE:
匿名ftp(化学数学软件等)
ftp://210.34.15.126/pub/
香港中文大学(使用Flashfxp的请将“使用被动模式”前面的勾取消就可以登录了。)
ftp://ftp.cs.cuhk.edu.hk
太原理工
ftp://202.207.240.119
ftp://fszc1@57333.com
ftp://fpdayl1@57333.com
交大FTP
ftp://cate:73610@202.117.28.20/
三明高等专科学校
ftp://smu:smc@218.5.241.10/
ftp://210.27.144.7/mpeg/
湖北国土资源职业学院下载
ftp://ftp.hbgt.com.cn/
浙江大学远程教学ftp
ftp://teach.zj.edu.cn
账号: ftpuser
密码: xt38kma7
浙江广播电视大学平阳分校FTP
ftp://61.153.14.194/
这个论坛实在是好呀,特把自己收藏的教育网内的ftp拿出来给大家分享!
1.
ftp://scau:scau@ftp1.scau.edu.cn:8021
;游戏+软件 (“使用被动模式”!)
2.
ftp://scau:scau@ftp2.scau.edu.cn:8021
;电视剧
3.
ftp://scau:scau@ftp3.scau.edu.cn:8021
;电影
一个学习的ftp,目录为:sf图书、Software、study,单线程,速度一般。
ftp://sf:xjtumba@swds.3322.org
一个高校的ftp,资料下载帐号 ,使用“被动模式”。
ftp://61.153.29.19
同济大学的电子书库FTP
ftp://202.120.165.151
成都理工大学
ftp://ftp.cdut.edu.cn
教授论坛FTP
ftp://202.113.31.180/
一高中的FTP里面有好多东东,速度200k
ftp://teacher:lg2005@ftp.lxyg.net
香港中文大学Ftp
ftp://ftp.cuhk.hk
FTP,(学习,游戏,电影)
ftp://211.86.94.57
ftp://211.86.49.66
ftp://edutc1.hyit.edu.cn
这3个指向同一目录。
ftp://210.27.234.1
ftp://202.117.47.49
这两也个指向同一目录
甘肃电大FTP
ftp://61.178.59.209/
内容:常用工具软件,电大数字图书馆,直播课堂资料,课件和四六级资料等
可以多线程下载,速度极快。
软件工具和教程类:
QUOTE:
超多软件下载一应俱全
ftp://ftp.hbgt.com.cn
一个软件FTP
ftp://61.132.90.12/
华军专用FTP
地址:
ftp://soft.pcsoft.com.cn
用户名:soft
密码:softsoft
很多软件,包括很多网管需要的软件(由TuTu2008提供)
ftp://61.152.101.129
用户名16288
密码都 16288
有效FTP包括LINUX和破解
ftp://fikusabe:928604@668y.com
发个超多软件的FTP,不用用户名的,不信的看图,看了的,就给顶下!
ftp://221.224.20.206
龙联论坛
ftp://down:bbs.51vip.net@downbbs.51vip.cn:29
ftp://218.6.43.31
软件和ASP源码FTP 刚发现的
ftp://downaspskynet:downaspskynet
@< wind_code_2 >
播放软件、插件、工具下载(非视频下载)
ftp://hdtvsoft:hdtvsoft@ftp.hd-tv.cn
学习教材! (lsq12345:使用迅雷依然能下。)
http://bbs.mumayi.net/viewthread
... &extra=page%3D1
小魔术视频表演、教程
ftp://fbitchs1@57333.com
电子图书书库 FTP下载3.12G(连接慢)
ftp://manao.xicp.net
杂志FTP(电子书)
ftp://tlfadv:deiejhjhhjt@tlfs90.3322.org:9988
深圳信息网FTP
ftp://cd:media@media.szwebs.net
驱动之家共享FTP
ftp://driversd:BBSDrivers1724@218.28.45.166/
常州市科技信息中心FTP
ftp://218.93.19.35/
--------------------------------
大量精彩FTP站点:
GNUCHINAFTP服务器
ftp.gnuchina.org
提供各种Linux*作系统镜像和应用软件供下载,不限制人数,但限制线程。
硅谷动力FTP服务器
ftp.esoftware.com.cn/
由ENET举办的FTP站点,提供各种共享软件、教程、代码下载,允许匿名访问和多线程下载。
中国下载FTP服务器1号
ftp.download.com.cn/
提供各种共享软件、免费软件供下载,允许匿名访问,人数限制2650。
中经网自由软件FTP服务器freesoft.cei.gov.cn/
由中国经济信息网举办,除提供了大量的Linux软件外,还设有众多的FTP站点镜像目录,允许匿
名访问。
中国下载FTP服务器2号ftp1.download.com.cn/
提供各种共享软件、免费软件供下载,允许匿名访问,人数限制1650,同时提供http网页导
航。
黄金眼FTP服务器202.205.10.22/
提供大量的游戏供下载,分为策略、动作、格斗、即时、角色、冒险、模拟、体育等目录,允许
匿名访问。
EastDoor亦多下载中心FTP服务器202.113.29.120/
提供Linux、Windows、Solaris、ISO文件等软件和电影供下载,允许匿名访问,每个IP最大线程
数3,人数限制100。
TurboLinuxFTP服务器
ftp.turbolinux.com.cn/
提供TurboLinux*作系统和各种Linux应用软件供下载,允许匿名访问。
蓝皮鼠临时FTP服务器cn-ftp.dhs.org/
提供各种软件、资料文档下载,允许匿名访问,单个IP线程最高为1。
深圳热线FTP服务器
ftp.szonline.net/
提供各种软件、游戏、编程资料下载,允许匿名访问,人数限制60。
中国经济信息网FTP服务器
ftp.cei.gov.cn/
提供经济类软件和研究资料下载,允许匿名访问。
中国工程技术FTP服务器
ftp.cetin.net.cn/
提供各种应用软件、编程软件供下载,允许匿名访问。
166.111.174.33(电影,软件)
166.111.184.48(电影,游戏,日剧)
166.111.215.143(东西很少,但是有机器猫1-49集下载)
166.111.215.175(东西不多,但是有不少大软件)
166.111.106.144(东西很杂,连上后进PUB目录,然后看内容提要吧)
166.111.107.91(音乐,软件)
166.111.136.248(好象都是一些3D的东东)
166.111.141.38(软件)
166.111.142.6(不少大的软件,音乐,CD就是放软件的目录)
166.111.147.51(音乐,特别是国外音乐很多,可惜限了速100K)
166.111.136.2(不少大软件和ISO的东东)
166.111.14.199(软件,电影,MP3,还有一些BOOK)
166.111.25.5(软件,电影,连续剧,走遍美国等……)
166.111.2.114(软件)
166.111.26.145(正在学习英文的朋友可以来这里,不少书喔)
166.111.33.64(东西很少,软件,电影)
166.111.36.151(不少好的软件喔..)
166.111.37.6(软件)
166.111.37.82(软件)
166.111.41.149(音乐)
166.111.41.172(音乐和一些电影的小片段)
166.111.49.106(书刊)
166.111.60.198(软件)
166.111.53.55(书刊,教程)
166.111.53.84(很多ISO的好东东)
166.111.55.29(软件)
166.111.61.200(电影)
166.111.61.232(军事)
166.111.62.19(不少ISO的大东东,其中包括有MSDN)
166.111.62.245(音乐)
166.111.70.51(软件)
posted @
2006-12-11 00:57 Jerry Cat 阅读(521) |
评论 (0) |
编辑 收藏
利用MFC的Csocket类实现网络通信
Mail
近年来,利用Internet进行网际间通讯,在WWW浏 览、FTP、Gopher这些常规服务,以及在网络电话、多媒体会议等这些对实时性要求严格 的应用中成为研究的热点,而且已经是必需的了。Windows环境下进行通讯程序设计的最基本方法是应用Windows Sockets实现进程间的通讯,为此微软提供了大量基于Windows Sockets的通讯API,如WinSockAPI、WinInetAPI和ISAPI,并一直致力于开发更快、 更容易的通讯API,将其和MFC集成在一起以使通讯编程越来越容易。本实例重点介绍使用MFC的CSocket类编写网络通讯程序的方法,并通过使用CSocket类实现了网络聊天程序。程序编译运行后的界面效果如图一所示:
一、实现方法
微软的MFC把复杂的WinSock API函数封装到类里,这使得编写网络应用程序更容易。CAsyncSocket类逐个封装了WinSock API,为高级网络程序员 提供了更加有力而灵活的方法。这个类基于程序员了解网络通讯的假设,目的是为了在MFC中使用WinSock,程序员有责任处理诸如阻塞、字节顺序和在Unicode与MBCS 间转换字符的任务。为了给程序员提供更方便的接口以自动处理这些任务,MFC给出 了CSocket类,这个类是由CAsyncSocket类继承下来的,它提供了比CAsyncSocket更高层的WinSock API接口。Csocket类和CsocketFile类可以与Carchive类一起合作来管理发送和接收的数据,这使管理数据收发更加便利。CSocket对象提供阻塞模式,这对于Carchive的同步操作是至关重要的。阻塞函数(如Receive()、Send()、ReceiveFrom()、SendTo() 和Accept())直到操作完成后才返回控制权,因此如果需要低层控制和高效率,就使用CasyncSock类;如果需要方便,则可使用Csocket类。
一些网络应用程序(如网络电话、多媒体会议工具)对实时性要求非常强,要求能够直接应用WinSock发送和接收数据。为了充分利用MFC 的优势,首选方案应当是MFC中的CAsyncSocket类或CSocket类,这两个类完全封装了WinSock API,并提供更多的便利。本实例介绍应用这两个类的编程模型,并引出相关的成员函数与一些概念的解释。
CSocket类是由CAsyncSocket继承而来的,事实上,在MFC中CAsyncSocket 逐个封装了WinSock API,每个CAsyncSocket对象代表一个Windows Socket对象,使用CAsyncSocket 类要求程序员对网络编程较为熟悉。相比起来,CSocket类是CAsyncSocket的派生类, 继承了它封装的WinSock API。
一个CSocket对象代表了一个比CAsyncSocket对象更高层次的Windows Socket的抽象,CSocket类与CSocketFile类和CArchive类一起工作来发送和接收数据,因此使用它更加容易使用。CSocket对象提供阻塞模式,因为阻塞功 能对于CArchive的同步操作是至关重要的。在这里有必要对阻塞的概念作一解释: 一个socket可以处于"阻塞模式"或"非阻塞模式",当一个套接字处于阻塞模式(即同步操作)时,它的阻塞函数直到操作完成才会返回控制权,之所以称为阻塞是因为此套接字的阻塞函数在完成操作返回之前什么也不能做。如果一个socket处于非阻塞模式(即异步操作),则会被调用函数立即返回。在CAsyncSocket类中可以用GetLastError 成员函数查询最后的错误,如果错误是WSAEWOULDBLOCK则说明有阻塞,而CSocket绝不会返回WSAEWOULDBLOCK,因为它自己管理阻塞。微软建议尽量使用非阻塞模式,通过网络事件的发生而通知应用程序进行相应的处理。但在CSocket类中,为了利用CArchive 处理通讯中的许多问题和简化编程,它的一些成员函数总是具有阻塞性质的,这是因为CArchive类需要同步的操作。
在Win32环境下,如果要使用具有阻塞性质的套接字,应该放在独立的工作线程中处理,利用多线程的方法使阻塞不至于干扰其他线程,也不会把CPU时间浪费在阻塞上。多线程的方法既可以使程序员享受CSocket带 来的简化编程的便利,也不会影响用户界面对用户的反应。
CAsyncSocket类编程模型
在一个MFC应用程序中,要想轻松处理多个网 络协议,而又不牺牲灵活性时,可以考虑使用CAsyncSocket类,它的效率比CSocket 类要高。CAsyncSocket类针对字节流型套接字的编程模型简述如下:
1、构造一个CAsyncSocket对象,并用这个 对象的Create成员函数产生一个Socket句柄。可以按如下两种方法构造:
CAsyncSocket sock; //使用默认参数产生一个字节流套接字 Sock.Create();
|
或在指定端口号产生一个数据报套接字
CAsyncSocket*pSocket=newCAsyncSocket; intnPort=27; pSocket->Create(nPort,SOCK-DGRAM);
|
第一种方法在栈上产生一个CAsyncSocket对象, 而第二种方法在堆上产生CAsyncSocket对象;第一种方法中Create()成员函数用缺省参数产生一个字节流套接字,第二种方法中用Create()成员函数在指定的端口产生一个数字报套接字。Create()函数的原型为:
BOOL Create( UINT nSocketPort = 0, int nSocketType = SOCK_STREAM, LPCTSTR lpszSocketAddress = NULL );
|
该函数的参数有:
1)端口,UINT类型。注意:如果是服务方,则使 用一个众所周知的端口供服务方连接;如果是客户方,典型做法是接受默认参数,使 套接字可以自主选择一个可用端口;
2)socket 类型,可以是SOCK-STREAM(默认值,字节流)或SOCK-DGRAM(数据报);
3)socket的地址,例如"
ftp.gliet.edu.cn
"或"202.193.64.33"。
2、如是客户方程序,用CAsyncSocket∷Connect()成员函数连接到服务方;如是服务方程序,用CAsyncSocket∷Listen()成员函数开始 监听,一旦收到连接请求,则调用CAsyncSocket∷Accept()成员函数开始接收。注意:CAsyncSocket ∷Accept()成员函数要用一个新的并且是空的CAsyncSocket对象作为它的参数,这里所说 的"空的"指的是这个新对象还没有调用Create()成员函数。
3、调用其他的CAsyncSocket类的Receive()、ReceiveFrom()、Send()和SendTo()等成员函数进行数据通信。
4、通讯结束后,销毁CAsyncSocket对象。如果是在栈上产生的CAsyncSocket对象,则对象超出定义的范围时自动被析构;如果是在堆上产生,也就是用了new这个操作符,则必须使用delete操作符销毁CAsyncSocket 对象。
CSocket类编程模型
使用CSocket对象涉及CArchive和CSocketFile 类对象。以下介绍的针对字节流型套接字的操作步骤中,只有第3步对于客户方和服务方操作是不同的,其他步骤都相同。
1、构造一个CSocket对象。
2、使用这个对象的Create()成员函数产生一个socket对象。在客户方程序中,除非需要数据报套接字,Create()函数一般情况下应该使用默认参数。而对于服务方程序,必须在调用Create时指定一个端口。需要注意的是,Carchive类对象不能与数据报(UDP)套接字一起工作,因此对于数据报套接字,CAsyncSocket和CSocket 的使用方法是一样的。
3、如果是客户方套接字,则调用CAsyncSocket ∷Connect()函数与服务方套接字连接;如果是服务方套接字,则调用CAsyncSocket∷Listen()开始监听来自客户方的连接请求,收到连接请求后,调用CAsyncSocket∷Accept()函数接受请求,建立连接。请注意Accept()成员函数需要一个新的并且为空的CSocket对象作为它的参数,解释同上。
4、产生一个CSocketFile对象,并把它与CSocket 对象关联起来。
5、为接收和发送数据各产生一个CArchive 对象,把它们与CSocketFile对象关联起来。切记CArchive是不能和数据报套接字一起工作的。
6、使用CArchive对象的Read()、Write()等函数在客户与服务方传送数据。
7、通讯完毕后,销毁CArchive、CSocketFile和CSocket对象。
二、编程步骤
1、 启动Visual C++6.0,生成一个基于对话框架的应用程序,将该程序命名为"Test";
2、 按照图一所示的效果图设置对话框的界面;
3、 使用Class Wizard为应用程序的按钮添加鼠标单击消息响应函数;
4、 使用Class Wizard在应用程序中定义新类CNewSocket,其基类选择为CSocket;
5、 添加代码,编译运行程序。
三、程序代码
////////////////////////////////////////////////// NewSocket.h : header file #if !defined(AFX_NEWSOCKET_H__8CE2ED73_1D56_11D3_9928_00A0C98F3E85__INCLUDED_) #define AFX_NEWSOCKET_H__8CE2ED73_1D56_11D3_9928_00A0C98F3E85__INCLUDED_ #if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000 class CTestDlg; #include <afxsock.h>
class CNewSocket : public CSocket { // Attributes public:
// Operations public: CNewSocket(); virtual ~CNewSocket();
// Overrides public: int m_Status; void GetDlg(CTestDlg *dlg); CTestDlg *m_dlg; // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CNewSocket) public: virtual void OnAccept(int nErrorCode); virtual void OnReceive(int nErrorCode); virtual void OnClose(int nErrorCode); //}}AFX_VIRTUAL // Generated message map functions //{{AFX_MSG(CNewSocket) // NOTE - the ClassWizard will add and remove member functions here. //}}AFX_MSG // Implementation protected: }; #endif
//////////////////////////////////////////////////////// NewSocket.cpp : implementation file #include "stdafx.h" #include "Test.h" #include "NewSocket.h" #include "TestDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif
CNewSocket::CNewSocket() {}
CNewSocket::~CNewSocket() {}
#if 0 BEGIN_MESSAGE_MAP(CNewSocket, CSocket) //{{AFX_MSG_MAP(CNewSocket) //}}AFX_MSG_MAP END_MESSAGE_MAP() #endif // 0
void CNewSocket::OnAccept(int nErrorCode) { if (m_dlg->m_ClientSocket==NULL) m_dlg->OnAccept(); CSocket::OnAccept(nErrorCode); }
void CNewSocket::OnReceive(int nErrorCode) { m_dlg->OnReceive(); CSocket::OnReceive(nErrorCode); }
void CNewSocket::GetDlg(CTestDlg *dlg) { m_dlg=dlg; }
void CNewSocket::OnClose(int nErrorCode) { m_dlg->OnClose(); CSocket::OnClose(nErrorCode); }
///////////////////////////////////////////////////////////////// TestDlg.h : header file #if !defined(AFX_TESTDLG_H__EDDDE196_1BF1_11D3_BE77_0000B454AEE4__INCLUDED_) #define AFX_TESTDLG_H__EDDDE196_1BF1_11D3_BE77_0000B454AEE4__INCLUDED_ #if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000 #include "NewSocket.h"
class CTestDlg : public CDialog { // Construction public: void SocketReset(); void OnClose(); void OnReceive(); void OnAccept(); CSocketFile *m_file; CArchive *m_arOut; CArchive *m_arIn; CNewSocket* m_ServerSocket; CNewSocket* m_ClientSocket; CTestDlg(CWnd* pParent = NULL); // standard constructor // Dialog Data //{{AFX_DATA(CTestDlg) enum { IDD = IDD_TEST_DIALOG }; CString m_Info; CString m_Output; CString m_Input; CString m_Connect; CString m_IPAddress; UINT m_Port; int m_Status; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CTestDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: HICON m_hIcon; // Generated message map functions //{{AFX_MSG(CTestDlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg void OnConnect(); afx_msg void OnDisconnect(); afx_msg void OnSend(); afx_msg void OnServerradio(); afx_msg void OnClientradio(); afx_msg void OnSendclear(); afx_msg void OnReceiveclear(); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; #endif
//////////////////////////////////////////////////////////////// TestDlg.cpp : implementation file #include "stdafx.h" #include "Test.h" #include "TestDlg.h" #include <afxsock.h> #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif
class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: //{{AFX_MSG(CAboutDlg) //}}AFX_MSG DECLARE_MESSAGE_MAP() };
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT }
void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP }
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAP END_MESSAGE_MAP()
CTestDlg::CTestDlg(CWnd* pParent /*=NULL*/) : CDialog(CTestDlg::IDD, pParent) { //{{AFX_DATA_INIT(CTestDlg) m_Info = _T(""); m_Output = _T(""); m_Input = _T(""); m_Connect = _T(""); m_IPAddress = _T(""); m_Port = 0; m_Status = -1; //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); }
void CTestDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CTestDlg) DDX_Text(pDX, IDC_OUTPUTEDIT, m_Output); DDX_Text(pDX, IDC_INPUTEDIT, m_Input); DDX_Text(pDX, IDC_CONNECTEDIT, m_Connect); DDX_Text(pDX, IDC_IPADDRESS, m_IPAddress); DDV_MaxChars(pDX, m_IPAddress, 15); DDX_Text(pDX, IDC_PORT, m_Port); DDX_Radio(pDX, IDC_SERVERRADIO, m_Status); //}}AFX_DATA_MAP }
BEGIN_MESSAGE_MAP(CTestDlg, CDialog) //{{AFX_MSG_MAP(CTestDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_CONNECTBUTTON, OnConnect) ON_BN_CLICKED(IDC_DISCONNECTBUTTON, OnDisconnect) ON_BN_CLICKED(IDC_SENDBUTTON, OnSend) ON_BN_CLICKED(IDC_SERVERRADIO, OnServerradio) ON_BN_CLICKED(IDC_CLIENTRADIO, OnClientradio) ON_BN_CLICKED(IDC_SENDCLEARBUTTON, OnSendclear) ON_BN_CLICKED(IDC_RECEIVECLEARBUTTON, OnReceiveclear) //}}AFX_MSG_MAP END_MESSAGE_MAP()
BOOL CTestDlg::OnInitDialog() { CDialog::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon m_Status=-1; m_ServerSocket=NULL; m_ClientSocket=NULL; m_arIn=NULL; m_arOut=NULL; m_file=NULL; m_Connect=""; m_IPAddress="202.207.243.29"; m_Port=5000; GetDlgItem(IDC_IPADDRESS)->EnableWindow(FALSE); GetDlgItem(IDC_PORT)->EnableWindow(FALSE); UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
void CTestDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } }
// If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CTestDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } }
// The system calls this to obtain the cursor to display while the user drags // the minimized window. HCURSOR CTestDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; }
void CTestDlg::OnConnect() { CString msg; UpdateData(TRUE); if (m_Status==0 ) //server { if ( m_ServerSocket!=NULL) { m_Connect="Please disconnect!"; UpdateData(FALSE); } else { m_Connect="Waiting for Client..."; UpdateData(FALSE); if(!AfxSocketInit()) { MessageBox("WindowsSocket initial failed!","Send",MB_ICONSTOP); return; } m_ServerSocket=new CNewSocket; m_ServerSocket->m_Status=m_Status; m_ServerSocket->GetDlg(this); if(!m_ServerSocket->Create(m_Port)) MessageBox("SendSocket create failed!", "Send",MB_ICONSTOP); else { m_ServerSocket->Listen(); } } } else { if (m_Status==1) { if (m_ClientSocket!=NULL) { m_Connect="Please disconnect!"; UpdateData(FALSE); } else { m_Connect="Connect to the Server..."; UpdateData(FALSE); if(!AfxSocketInit()) { MessageBox("WindowsSocket initial failed!","Receive",MB_ICONSTOP); return; } m_ClientSocket=new CNewSocket; m_ClientSocket->GetDlg(this); m_ClientSocket->m_Status=m_Status; if(!m_ClientSocket->Create()) { MessageBox("ReceiveSocket create failed!","Receive",MB_ICONSTOP); return; } else { if (!m_ClientSocket->Connect(m_IPAddress,m_Port)) { CString str=m_Connect; SocketReset(); m_Connect=str; m_Connect+="Error!"; UpdateData(FALSE); } else { m_Connect+="OK!"; m_file=new CSocketFile(m_ClientSocket); m_arIn=new CArchive(m_file, CArchive::load); m_arOut=new CArchive(m_file, CArchive::store); } UpdateData(FALSE); } } } } if (m_Status==-1) { msg="Please choose the status!"; AfxMessageBox(msg); } }
void CTestDlg::OnSend() { if (m_arOut) { if (m_Status==0) { UpdateData(TRUE); *m_arOut<<m_Output; m_arOut->Flush(); } else { UpdateData(TRUE); *m_arOut<<m_Output; m_arOut->Flush(); } } else AfxMessageBox("Not connected!"); }
void CTestDlg::OnAccept() { m_Connect+="OK!"; UpdateData(FALSE); m_ClientSocket=new CNewSocket; m_ClientSocket->GetDlg(this); m_ServerSocket->Accept(*m_ClientSocket); m_ClientSocket->m_Status=m_ServerSocket->m_Status; m_file=new CSocketFile(m_ClientSocket); m_arIn=new CArchive(m_file, CArchive::load); m_arOut=new CArchive(m_file, CArchive::store); }
void CTestDlg::OnReceive() { *m_arIn>>m_Input; UpdateData(FALSE); }
void CTestDlg::OnDisconnect() { if (m_arOut!=NULL) { SocketReset(); m_Connect="Disconnected!"; UpdateData(FALSE); } }
void CTestDlg::OnClose() { if (m_ClientSocket->m_Status==0) m_Connect="Client "; else m_Connect="Server "; m_Connect+="has disconnected!"; UpdateData(FALSE); }
void CTestDlg::SocketReset() { if (m_arIn!=NULL) { delete m_arIn; m_arIn=NULL; } if (m_arOut!=NULL) { delete m_arOut; m_arOut=NULL; } if (m_file!=NULL) { delete m_file; m_file=NULL; } if (m_ClientSocket!=NULL) { delete m_ClientSocket; m_ClientSocket=NULL; } if (m_ServerSocket!=NULL) { delete m_ServerSocket; m_ServerSocket=NULL; } m_Connect=""; UpdateData(FALSE); }
void CTestDlg::OnServerradio() { UpdateData(TRUE); GetDlgItem(IDC_IPADDRESS)->EnableWindow(FALSE); GetDlgItem(IDC_PORT)->EnableWindow(TRUE); UpdateData(FALSE); }
void CTestDlg::OnClientradio() { UpdateData(TRUE); GetDlgItem(IDC_IPADDRESS)->EnableWindow(TRUE); GetDlgItem(IDC_PORT)->EnableWindow(TRUE); UpdateData(FALSE); }
void CTestDlg::OnSendclear() { m_Output=""; UpdateData(FALSE); }
void CTestDlg::OnReceiveclear() { m_Input=""; UpdateData(FALSE); }
|
四、小结
本实例介绍了CAsyncSocket、CSocket类,并通过使用CSocket类实现了网络聊天程序。读者朋友还可以通过MFC CArchive 对象进行信息的接发操作,使得网络传输如同使用MFC的文档连载协议(Serialization protocol),简捷易用。
|
posted @
2006-12-11 00:43 Jerry Cat 阅读(577) |
评论 (3) |
编辑 收藏
/********************************************\
| 欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉! |
\********************************************/
C++对象模型(14) -
3.6 Pointer to Data Members
作者: Jerry Cat
时间: 2006/11/23
链接:
http://www.cppblog.com/jerysun0818/archive/2006/11/23/15593.html
3.6 Pointer to Data Members:
;-----------------------------------------------------------------------
Consider the following Point3d class declaration. It declares a virtual function, a static data member, and three coordinate values:
class Point3d {
public:
virtual ~Point3d(); //虚表指针的位置"非头即尾"
// ...
protected:
static Point3d origin;
float x, y, z;
};
What does it mean, then, to take the address of one of the coordinate members? For example, what value should the following yield?
&3d_point::z;
It is going to yield the z-coordinate's offset within the class object. Minimally, this has to be the size of the x and y members, since the language requires the members within an access level be set down in the order of declaration.
3.6 Pointer to Data Members
Pointers to data members are a somewhat arcane but useful feature of the language, particularly if you need to probe at the underlying member layout of a class. One example of such a probing might be to determine if the vptr is placed at the beginning or end of the class. A second use, presented in Section 3.2, might be to determine the ordering of access sections within the class. As I said, it's an arcane, although potentially useful, language feature.
Consider the following Point3d class declaration. It declares a virtual function, a static data member, and three coordinate values:
class Point3d {
public:
virtual ~Point3d();
// ...
protected:
static Point3d origin;
float x, y, z;
};
The member layout for each Point3d class object contains the three coordinate values in the order x, y, z and a vptr. (Recall that origin, the static data member, is hoisted outside the individual class object.) The only implementation aspect of the layout is the placement of the vptr. The Standard permits the vptr to be placed anywhere within the object: at the beginning, at the end, or in between either of the three members. In practice, all implementations place it either at the beginning or at the end.
What does it mean, then, to take the address of one of the coordinate members? For example, what value should the following yield?
&3d_point::z;
It is going to yield the z-coordinate's offset within the class object. Minimally, this has to be the size of the x and y members, since the language requires the members within an access level be set down in the order of declaration.
At the compiler's discretion, however, the vptr may be placed either before, in-between, or after the coordinate members. Again, in practice, the vptr is either placed at the beginning or at the end of the class object. On a 32-bit machine, floats are 4 bytes each, so we would expect the value to be either 8 bytes without an intervening vptr or 12 bytes with it. (The vptr, and pointers in general, use 4 bytes on a 32-bit architecture.)
That expectation, however, is off by one—a somewhat traditional error for both C and C++ programmers.
The physical offset of the three coordinate members within the class layout are, respectively, either 0, 4, and 8 if the vptr is placed at the end or 4, 8, and 12 if the vptr is placed at the start of the class. The value returned from taking the member's address, however, is always bumped up by 1. Thus the actual values are 1, 5, and 9, and so on. Do you see why Bjarne decided to do that?
The problem is distinguishing between a pointer to no data member and a pointer to the first data member. Consider for example:
float Point3d::*p1 = 0;
float Point3d::*p2 = &Point3d::x;
// oops: how to distinguish?
if ( p1 == p2 ) {
cout << " p1 & p2 contain the same value — ";
cout << " they must address the same member!" << endl;
}
To distinguish between p1 and p2, each actual member offset value is bumped up by 1. Hence, both the compiler (and the user) must remember to subtract 1 before actually using the value to address a member.
Given what we now know about pointers to data members, we find that explaining the difference between
&Point3d::z;
and
&origin.z
is straightforward. Whereas taking the address of a nonstatic data member yields its offset within the class, taking the address of a data member bound to an actual class object yields the member's actual address in memory. The result of
&origin.z
adds the offset of z (minus 1) to the beginning address of origin. origin是个实例化的类Point3d的静态数据成员. The value returned is of type
float*
not
float Point3d::*
because it refers to an specific single instance(静态成员属于类而非类的各具体实例对象), much the same as taking the address of a static data member.
Under multiple inheritance, the combination of a second (or subsequent) base class pointer to a member bound to a derived class object is complicated by the offset that needs to be added. For example, if we have
struct Base1 { int val1; };
struct Base2 { int val2; };
struct Derived : Base1, Base2 { ... };
void func1( int d::*dmp, d *pd )
{
// expects a derived pointer to member
// what if we pass it a base pointer?
pd->*dmp;
}
void func2( d *pd )
{
// assigns bmp 1
int b2::*bmp = &b2::val2;
// oops: bmp == 1,
// but in Derived, val2 == 5
func1( bmp, pd )
}
bmp must be adjusted by the size of the intervening Base1 class when passed as the first argument to func1(). Otherwise, the invocation of
pd->*dmp;
within func1() will access Base1::val1, not Base2::val2 as the programmer intended. The specific solution in this case is
// internal transformation by compiler
func1( bmp + sizeof( Base1 ), pd );
In general, however, we cannot guarantee that bmp is not 0 and so must guard against it:
// internal transformation
// guarding against bmp == 0
func1( bmp ? bmp + sizeof( Base1 ) : 0, pd );
二. Efficiency of Pointers to Members:
The following sequence of tests attempts to gain some measure of the overhead associated with using pointers to members under the various class representations of the 3D point. In the first two cases, there is no inheritance. The first case takes the address of a bound member:
float *ax = &pA.x;
for the three coordinate members of points pA and pB. The assignment, addition, and subtraction look as follows:
*bx = *ax - *bz;
*by = *ay + *bx;
*bz = *az + *by;
The second case takes the address of a pointer to data member:
float pt3d::*ax = &pt3d::x;
for the three coordinate members. The assignment, addition, and subtraction use the pointer to data member syntax, binding the values to the objects pA and pB:
pB.*bx = pA.*ax - pB.*bz;
pB.*by = pA.*ay + pB.*bx;
pB.*bz = pA.*az + pB.*by;
Recall that the direct data member exercise of this function, executed in Section 3.5, ran with an average user time of 0.80 with optimization turned on and 1.42 with optimization turned off for both compilers. The results of running these two tests, coupled with the results of the direct data access, are shown in Table 3.3:
Table 3.3. Nonstatic Data Member Access
Optimized Non-optimized
Direct Access 0.80 1.42
Pointer to
Bound Member 0.80 3.04
Pointer to
Data Member
CC 0.80 5.34
NCC 4.04 5.34
The non-optimized results conform to expectations. That is, the addition of one indirection per member access through the bound pointer more than doubles the execution time. The pointer-to-member access again nearly doubles the execution time. The binding of the pointer to data member to the class object requires the addition of the offset minus 1 to the address of the object. More important, of course, the optimizer is able to bring the performance of all three access strategies into conformance, except the anomalous behavior of the NCC optimizer. (It is interesting to note here that the appalling performance of the NCC executable under optimization reflects a poor optimization of the generated assembly code and not an attribute of the source-level C++ code. An examination of the generated non-optimized assembly for both CC and NCC showed the two outputs to be identical.)
The next set of tests looks at the impact of inheritance on the performance of pointers to data members. In the first case, the independent Point class is redesigned into a three-level single inheritance hierarchy with one coordinate value as a member of each class:
class Point { ... }; // float x;
class Point2d : public Point { ... }; // float y;
class Point3d : public Point2d { ... }; // float z;
The next representation retains the three-level single inheritance hierarchy but introduces one level of virtual inheritance: the Point2d class is virtually derived from Point. As a result, each access of Point::x is now accessing a virtual base class data member. Then, more out of curiosity than need, the final representation added a second level of virtual inheritance, that of Point3d being virtually derived from Point2d. Table 3.4 shows the results. (Note: The poor performance of the NCC optimizer was consistent across the tests, so I've left it off the listing.)
Table 3.4. Pointer to Data Member Access
Optimized % Non-optimized
No Inheritance 0.80 5.34
SI (3 levels) 0.80 5.34
VI (1 level) 1.60 5.44
VI (2 level) 2.14 5.51
SI: Single Inheritance VI: Virtual Inheritance
Because inherited data members are stored directly within the class object, the introduction of inheritance does not affect the performance of the code at all. The major impact of introducing virtual inheritance is to impede the effectiveness of the optimizer. Why? In these two implementations, each level of virtual inheritance introduces an additional level of indirection. Under both implementations, each access of Point::x, such as
pB.*bx
is translated into
&pB->__vbcPoint + ( bx - 1 )
rather than the more direct
&pB + ( bx - 1 )
The additional indirection reduced the ability of the optimizer to move all the processing into registers.
posted @
2006-11-23 20:23 Jerry Cat 阅读(535) |
评论 (0) |
编辑 收藏
用内联汇编调虚函数, 理解VTABLE原理
虚函数和动态绑定是C++面向对象编程的核心内容之一。要理解C++虚函数的调用本质,就不得不说VPTR和VTABLE。所有拥有虚函数的C++类的大小都比可看到的内容多至少4个字节(如果派生树中存在多继承,就可能多于4个字节),这多出来的4个字节就是VPTR,它位于每个实例的最前方。VPTR的内容就是一个unsigned int的地址,指向一个内存区域,而这个被指向的内存区域就是VTABLE,所谓虚函数地址表。每个拥有虚函数的类都拥有一张VTABLE,里面是一个函数指针数组,每4个字节为一个单位,指向虚函数的入口地址。C++在调用虚函数时,首先要通过这个类的实例内容看到VPTR,从而找到VTABLE,然后根据要调用的虚函数,取相应偏移地址的内容,从而把调用转到这个位置。
以下是我用VC内联汇编编写的一个手工模拟此过程的示例,通过例子中的代码可以清楚的看出虚函数的调用过程。首先定义一个类,它有一个虚函数:
posted @
2006-11-19 04:13 Jerry Cat 阅读(1352) |
评论 (4) |
编辑 收藏
/********************************************\
| 欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉! |
\********************************************/
C++对象模型(13) -
3.5 Object Member Efficiency
作者: Jerry Cat
时间: 2006/11/17
链接:
http://www.cppblog.com/jerysun0818/archive/2006/11/17/15311.html
3.5 Object Member Efficiency
;-----------------------------------------------------------------------
An obvious observation is that without the optimizer turned on, it is extremely difficult to guess at the performance characteristics of a program, since the code is potentially hostage to the "quirk(s) of code generation…unique to a particular compiler." Before one begins source level "optimizations" to speed up a program, one should always do actual performance measurements rather than relying on speculation and common sense.不要想当然, 要试验之.
In the next sequence of tests, I introduced first a three-level single inheritance representation of the Point abstraction and then a virtual inheritance representation of the Point abstraction. I tested both direct and inline access (multiple inheritance did not fit naturally into the model, so I decided to forego it.) The general hierarchy is
class Point1d {...}; // maintains x
class Point2d : public Point1d {...}; // maintains y
class Point3d : public Point2d {...}; // maintains z
The one-level virtual inheritance derived Point2d virtually from Point1d. The two-level virtual inheritance additionally derived Point3d virtually from Point2d. Table 3.2 lists the results of running the tests for both compilers. (Again, I break out the times for the two compilers only when their performances differ significantly from each other's.)
Table 3.2. Data Access under Inheritance Models
Optimized Non-optimized
Single Inheritance
Direct Access 0.80 1.42
Inline Methods
CC 0.80 2.55
NCC 0.80 3.10
Virtual Inheritance — 1-Level
Direct Access 1.60 1.94
Inline Methods
CC 1.60 2.75
NCC 1.60 3.30
Virtual Inheritance — 2-Level
Direct Access
CC 2.25 2.74
NCC 3.04 3.68
Inline Methods
CC 2.25 3.22
NCC 2.50 3.81
Single inheritance should not affect the test performance, since the members are stored contiguously within the derived class object and their offsets are known at compile time. The results, as expected, were exactly the same as those of the independent abstract data type. (The same should be true under multiple inheritance, but I didn't confirm that.)
Again, it is worth noting that with the optimizer off, performance, which common sense says should be the same (direct member access versus inline access), is in practice slower in the case of inline functions. The lesson again is that the programmer concerned with efficiency must actually measure the performance of his or her program and not leave the measurement of the program to speculation and assumption. It is also worth noting that optimizers don't always work. I've more than once had compilations fail with an optimizer turned on that compiled fine "normally."别想当然, 实验之! 编译时尽可能打开优化开关.
The virtual inheritance performance is disappointing in that neither compiler recognized that the access of the inherited data member pt1d::_x is through a nonpolymorphic class object and that therefore indirect runtime access is unnecessary. Both compilers generate indirect access of pt1d::_x (and pt1d::y in the case of two levels of virtual inheritance), even though its location within the two Point3d objects is fixed at compile time. The indirection significantly inhibited the optimizer's ability to move all the operations within registers. The indirection did not affect the non-optimized executables significantly.
虚继承导致性能大降, 即使打开优化开关也没太大起色.
posted @
2006-11-17 18:11 Jerry Cat 阅读(404) |
评论 (0) |
编辑 收藏
/********************************************\
| 欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉! |
\********************************************/
C++对象模型(12) - 3.4
Inheritance and the Data Member
作者: Jerry Cat
时间: 2006/11/16
链接:
http://www.cppblog.com/jerysun0818/archive/2006/11/16/15269.html
3.4 Inheritance and the Data Member
;-----------------------------------------------------------------------
Under the C++ inheritance model, a derived class object is represented as the concatenation of its members with those of its base class(es). The actual ordering of the derived and base class parts is left unspecified by the Standard. In theory, a compiler is free to place either the base or the derived part first in the derived class object. In practice, the base class members always appear first, except in the case of a virtual base class. (In general, the handling of a virtual base class is an exception to all generalities, even, of course, this one.)
class Concrete1 {
public:
// ...
protected:
int val;
char bit1;
};
class Concrete2 : public Concrete1 {
public:
// ...
protected:
char bit2;
};
class Concrete3 : public Concrete2 {
public:
// ...
protected:
char bit3;
};
From a design standpoint, this representation may make more sense. From an implementation standpoint, however, we may be distressed to find that a Concrete3 class object now has a size of 16 bytes—double its previous size.
What's going on? Recall that the issue is the integrity of the base class subobject within the derived class. Let's walk through the layout of the inheritance hierarchy to see what is going on.
The Concrete1 class contains the two members—val and bit1—that together take up 5 bytes. The size of a Concrete1 class object, however, is 8 bytes: the 5 bytes of actual size plus 3 bytes of padding to align the object on a machine word boundary. That's as true in C as it is in C++; generally, alignment constraints are determined by the underlying processor.
粗心的程序员可要倒霉咯!
Nothing necessarily to complain about so far. It's the layout of the derived class that typically drives the unwary programmer into fits of either perplexity or angry indignation. Concrete2 adds a single nonstatic data member, bit2, of type char. Our unwary programmer expects it to be packed into the base Concrete1 representation, taking up one of the bytes otherwise wasted as alignment padding. This layout strategy makes the Concrete2 class object also of size 8 bytes, with 2 bytes of padding.
The layout of the Concrete2 class, however, instead preserves the 3 bytes of padding within the Concrete1 base class subobject. The bit2 member is set down after that, followed by an additional 3 bytes of padding. The size of a Concrete2 class object is 12 bytes, not 8, with 6 bytes wasted for padding. The same layout algorithm results in a Concrete3 class object's being 16 bytes, 9 of which are wasted on padding.
Why? Let's declare the following set of pointers:
Concrete2 *pc2;
Concrete1 *pc1_1, *pc2_2;
Both pc1_1 and pc2_2 can address objects of either three classes. The following assignment
*pc1_1 = *pc2_2;
should perform a default memberwise copy of the Concrete1 portion of the object addressed. If pc1_1 addresses a Concrete2 or Concrete3 object, that should not be of consequence to the assignment of its Concrete1 subobject.
However, if the language were to pack the derived class members Concrete2::bit2 or Concrete3::bit3 into the Concrete1 subobject, these language semantics could not be preserved. An assignment such as
pc1_1 = pc2;
// oops: derived class subobject is overridden
// its bit2 member now has an undefined value
*pc1_1 = *pc2_2;
would overwrite the values of the packed inherited members. It would be an enormous effort on the user's part to debug this, to say the least.
二. Adding Polymorphism:
If we want to operate on a point independent of whether it is a Point2d or Point3d instance, we need to provide a virtual function interface within our hierarchy. Let's see how things change when we do that:
class Point2d {
public:
Point2d( float x = 0.0, float y = 0.0 )
: _x( x ), _y( y ) {};
// access functions for x & y same as above
// invariant across type: not made virtual
// add placeholders for z — do nothing ...
virtual float z(){ return 0.0 };
virtual void z( float ) {}
// turn type explicit operations virtual
virtual void
operator+=( const Point2d& rhs ) {
_x += rhs.x(); _y += rhs.y(); }
// ... more members
protected:
float _x, _y;
};
It makes sense to introduce a virtual interface into our design only if we intend to manipulate two- and three-dimensional points polymorphically, that is, to write code such as
where p1 and p2 may be either two- or three-dimensional points. This is not something that any of our previous designs supported. This flexibility, of course, is at the heart of OO programming. Support for this flexibility, however, does introduce a number of space and access-time overheads for our Point2d class:
(1). Introduction of a virtual table associated with Point2d to hold the address of each virtual function it declares. The size of this table in general is the number of virtual functions declared plus an additional one or two slots to support runtime type identification.
(2). Introduction of the vptr within each class object. The vptr provides the runtime link for an object to efficiently find its associated virtual table.
(3). Augmentation of the constructor to initialize the object's vptr to the virtual table of the class. Depending on the aggressiveness of the compiler's optimization, this may mean resetting the vptr within the derived and each base class constructor. (This is discussed in more detail in Chapter 5.)
(4). Augmentation of the destructor to reset the vptr to the associated virtual table of the class. (It is likely to have been set to address the virtual table of the derived class within the destructor of the derived class. Remember, the order of destructor calls is in reverse: derived class and then base class.) An aggressive optimizing compiler can suppress a great many of these assignments.
Here is our new Point3d derivation:
class Point3d : public Point2d {
public:
Point3d( float x = 0.0, float y = 0.0, float z = 0.0 )
: Point2d( x, y ), _z( z ) {};
float z() { return _z; }
void z( float newZ ) { _z = newZ; }
void operator+=( const Point2d& rhs ) {
Point2d::operator+=( rhs );
_z += rhs.z();
}
// ... more members
protected:
float _z;
};
Although the syntax of the class's declaration has not changed, everything about it is now different: The two z() member functions and the operator+=() operator are virtual instances. Each Point3d class object contains an additional vptr member object (the instance inherited from Point2d). There is also a Point3d virtual table. The invocation of each member function made virtual is also more complex (this is covered in Chapter 4).
Placing the vptr at the start of the class is more efficient in supporting some virtual function invocations through pointers to class members under multiple inheritance (see Section 4.4). Otherwise, not only must the offset to the start of the class be made available at runtime, but also the offset to the location of the vptr of that class must be made available. The trade-off, however, is a loss in C language interoperability.
三. Multiple Inheritance:
Single inheritance provides a form of "natural" polymorphism regarding the conversion between base and derived types within the inheritance hierarchy. Look at Figures 3.1(b), 3.2(a), or 3.3, where you can see that the base and derived class objects both begin at the same address. They differ in that the derived object extends the length of its nonstatic data members. The assignment, such as
Point3d p3d;
Point2d *p = &p3d;
of the derived class object to a pointer or reference to the base class (regardless of the depth of the inheritance hierarchy) requires no compiler intervention or modification of the address. Instead, it happens "naturally," and in that sense, it provides optimal runtime efficiency.
From Figure 3.2(b), note that placing the vptr at the beginning of the class object breaks the natural polymorphism of single inheritance in the special case of a base class without virtual functions and a derived class with them. The conversion of the derived object to the base in this case requires the intervention of the compiler in order to adjust the address being assigned by the size of the vptr. Under both multiple and virtual inheritances, the need for compiler intervention is considerably more pronounced.
Multiple inheritance is neither as well behaved nor as easily modeled as single inheritance. The complexity of multiple inheritance lies in the "unnatural" relationship of the derived class with its second and subsequent base class subobjects. Consider, for example, the following multiply derived class, Vertex2d:
class Point2d {
public:
// ...
protected:
float _x, _y;
};
class Vertex {
public:
// ...
protected:
Vertex *next;
};
class Vertex2d :
public Point2d, public Vertex {
public:
//...
protected:
float mumble;
};
The problem of multiple inheritance primarily affects conversions between the derived and second or subsequent base class objects, either directly
extern void mumble( const Vertex& );
Vertex3d v;
...
// conversion of a Vertex3d to Vertex is ``unnatural''
mumble( v );
or through support for the virtual function mechanism. The problems with supporting virtual function invocation are discussed in Section 4.2.
The assignment of the address of a multiply derived object to a pointer of its leftmost (that is, first) base class is the same as that for single inheritance, since both point to the same beginning address. The cost is simply the assignment of that address (Figure 3.4 shows the multiple inheritance layout). The assignment of the address of a second or subsequent base class, however, requires that that address be modified by the addition (or subtraction in the case of a downcast) of the size of the intervening base class subobject(s).
What about access of a data member of a second or subsequent base class? Is there an additional cost? No. The member's location is fixed at compile time. Hence its access is a simple offset the same as under single inheritance regardless of whether it is a pointer, reference, or object through which the member is being accessed.
四. Virtual Inheritance:
A semantic side effect of multiple inheritance is the need to support a form of shared subobject inheritance. The classic example of this is the original iostream library implementation:
//pre-standard iostream implementation
class ios { ... };
class istream : public ios { ... };
class ostream : public ios { ... };
class iostream :
public istream, public ostream { ... };
Both the istream and ostream classes contain an ios subobject. In the layout of iostream, however, we need only a single ios subobject. The language level solution is the introduction of virtual inheritance:
class ios { ... };
class istream : public virtual ios { ... };
class ostream : public virtual ios { ... };
class iostream :
public istream, public ostream { ... };
The general implementation solution is as follows. A class containing one or more virtual base class subobjects, such as istream, is divided into two regions: an invariant region and a shared region. Data within the invariant region remains at a fixed offset from the start of the object regardless of subsequent derivations. So members within the invariant region can be accessed directly. The shared region represents the virtual base class subobjects. The location of data within the shared region fluctuates with each derivation. So members within the shared region need to be accessed indirectly. What has varied among implementations is the method of indirect access. The following example illustrates the three predominant strategies. Here is the data portion of a virtual Vertex3d inheritance hierarchy:
class Point2d {
public:
...
protected:
float _x, _y;
};
class Vertex : public virtual Point2d {
public:
...
protected:
Vertex *next;
};
class Point3d : public virtual Point2d {
public:
...
protected:
float _z;
};
class Vertex3d :
public Point3d, public Vertex {
public:
...
protected:
float mumble;
};
The general layout strategy is to first lay down the invariant region of the derived class and then build up the shared region.
However, one problem remains: How is the implementation to gain access to the shared region of the class? In the original cfront implementation, a pointer to each virtual base class is inserted within each derived class object. Access of the inherited virtual base class members is achieved indirectly through the associated pointer. For example, if we have the following Point3d operator:
void
Point3d::
operator+=( const Point3d &rhs )
{
_x += rhs._x;
_y += rhs._y;
_z += rhs._z;
};
under the cfront strategy, this is transformed internally into
// Pseudo C++ Code
__vbcPoint2d->_x += rhs.__vbcPoint2d->_x;
__vbcPoint2d->_y += rhs.__vbcPoint2d->_y;
_z += rhs._z;
A conversion between the derived and base class instances, such as
Vertex *pv = pv3d;
under the cfront implementation model becomes
// Pseudo C++ code
Vertex *pv = pv3d ? pv3d->__vbcPoint2d : 0;
3.4 Inheritance and the Data Member
Under the C++ inheritance model, a derived class object is represented as the concatenation of its members with those of its base class(es). The actual ordering of the derived and base class parts is left unspecified by the Standard. In theory, a compiler is free to place either the base or the derived part first in the derived class object. In practice, the base class members always appear first, except in the case of a virtual base class. (In general, the handling of a virtual base class is an exception to all generalities, even, of course, this one.)
Given this inheritance model, one can ask: What is the difference in providing two abstract data types for the representation of two- and three-dimensional points, such as
// supporting abstract data types
class Point2d {
public:
// constructor(s)
// operations
// access functions
private:
float x, y;
};
class Point3d {
public:
// constructor(s)
// operations
// access functions
private:
float x, y, z;
};
and providing a two- or three-level hierarchy in which each additional dimension is a class derived from the lower dimension? In the following subsections, the effects of single inheritance without the support of virtual functions, single inheritance with virtual functions, multiple inheritance, and virtual inheritance are examined. Figure 3.1(a) pictures the layout of Point2d and Point3d objects. (In the absence of virtual functions, they are equivalent to C struct declarations.)
Figure 3.1(a). Data Layout: Independent Structs
Inheritance without Polymorphism
Imagine that the programmer wishes to share an implementation but continue to use type-specific instances of either the two- or three-dimensional point. One design strategy is to derive Point3d from our Point2d class, with Point 3d inheriting all the operations and maintenance of the x- and y-coordinates. The effect is to localize and share data and the operations upon that data among two or more related abstractions. In general, concrete inheritance adds no space or access-time overhead to the representation.
class Point2d {
public:
Point2d( float x = 0.0, float y = 0.0 )
: _x( x ), _y( y ) {};
float x() { return _x; }
float y() { return _y; }
void x( float newX ) { _x = newX; }
void y( float newY ) { _y = newY; }
void operator+=( const Point2d& rhs ) {
_x += rhs.x();
_y += rhs.y();
}
// ... more members
protected:
float _x, _y;
};
// inheritance from concrete class
class Point3d : public Point2d {
public:
Point3d( float x = 0.0, float y = 0.0, float z = 0.0 )
: Point2d( x, y ), _z( z ) {};
float z() { return _z; }
void z( float newZ ) { _z = newZ; }
void operator+=( const Point3d& rhs ) {
Point2d::operator+=( rhs );
_z += rhs.z();
}
// ... more members
protected:
float _z;
};
The benefit of this design strategy is the localization of the code to manage the x- and y-coordinates. In addition, the design clearly indicates the tight coupling of the two abstractions. The declaration and use of both Point2d and Point3d class objects do not change from when the two classes were independent, so clients of these abstractions need not be aware of whether the objects are independent class types or related through inheritance. Figure 3.1(b) shows the layout of the Point2d and Point3d inheritance layout without the declaration of a virtual interface.
Figure 3.1(b). Data Layout: Single Inheritance without Virtual Functions
What are the possible pitfalls of transforming two independent classes into a type/subtype relationship through inheritance? A naive design might, in fact, double the number of function calls to perform the same operations. That is, say the constructor or operator+=() in our example were not made inline (or the compiler could not for some reason support the inlining of the member functions). The initialization or addition of a Point3d object would be the cost of the partial Point2d and Point3d instances. In general, choosing candidate functions for inlining is an important, if unglamorous, aspect of class design. Confirming that they are in fact inlined is necessary before final release of the implementation.
A second possible pitfall in factoring a class into a two-level or deeper hierarchy is a possible bloating of the space necessary to represent the abstraction as a class hierarchy. The issue is the language guarantee of the integrity of the base class subobject within the derived class. It's slightly subtle. A walk-through of an example might best explain it. Let's begin with a concrete class:
class Concrete {
public:
// ...
private:
int val;
char c1;
char c2;
char c3;
};
On a 32-bit machine, the size of each Concrete class object is going to be 8 bytes, broken down as follows:
4 bytes for val
1 byte each for c1, c2, and c3
1 byte for the alignment of the class on a word boundary
Say, after some analysis, we decide that a more logical representation splits Concrete into a three-level inheritance hierarchy as follows:
class Concrete1 {
public:
// ...
protected:
int val;
char bit1;
};
class Concrete2 : public Concrete1 {
public:
// ...
protected:
char bit2;
};
class Concrete3 : public Concrete2 {
public:
// ...
protected:
char bit3;
};
From a design standpoint, this representation may make more sense. From an implementation standpoint, however, we may be distressed to find that a Concrete3 class object now has a size of 16 bytes—double its previous size.
What's going on? Recall that the issue is the integrity of the base class subobject within the derived class. Let's walk through the layout of the inheritance hierarchy to see what is going on.
The Concrete1 class contains the two members—val and bit1—that together take up 5 bytes. The size of a Concrete1 class object, however, is 8 bytes: the 5 bytes of actual size plus 3 bytes of padding to align the object on a machine word boundary. That's as true in C as it is in C++; generally, alignment constraints are determined by the underlying processor.
Nothing necessarily to complain about so far. It's the layout of the derived class that typically drives the unwary programmer into fits of either perplexity or angry indignation. Concrete2 adds a single nonstatic data member, bit2, of type char. Our unwary programmer expects it to be packed into the base Concrete1 representation, taking up one of the bytes otherwise wasted as alignment padding. This layout strategy makes the Concrete2 class object also of size 8 bytes, with 2 bytes of padding.
The layout of the Concrete2 class, however, instead preserves the 3 bytes of padding within the Concrete1 base class subobject. The bit2 member is set down after that, followed by an additional 3 bytes of padding. The size of a Concrete2 class object is 12 bytes, not 8, with 6 bytes wasted for padding. The same layout algorithm results in a Concrete3 class object's being 16 bytes, 9 of which are wasted on padding.
"That's stupid," is the unwary programmer's judgment, which more than one has chosen to share with me over e-mail, on the phone, and in per-son. Do you see why the language behaves as it does?
Let's declare the following set of pointers:
Concrete2 *pc2;
Concrete1 *pc1_1, *pc2_2;
Both pc1_1 and pc2_2 can address objects of either three classes. The following assignment
*pc1_1 = *pc2_2;
should perform a default memberwise copy of the Concrete1 portion of the object addressed. If pc1_1 addresses a Concrete2 or Concrete3 object, that should not be of consequence to the assignment of its Concrete1 subobject.
However, if the language were to pack the derived class members Concrete2::bit2 or Concrete3::bit3 into the Concrete1 subobject, these language semantics could not be preserved. An assignment such as
pc1_1 = pc2;
// oops: derived class subobject is overridden
// its bit2 member now has an undefined value
*pc1_1 = *pc2_2;
would overwrite the values of the packed inherited members. It would be an enormous effort on the user's part to debug this, to say the least.
Adding Polymorphism
If we want to operate on a point independent of whether it is a Point2d or Point3d instance, we need to provide a vi