Archive for 五月, 2007

对象参数的运行机制剖析(C++学习笔记)

Posted by 机器人 on 31st 五月 2007 in c/c++

在以值的方式向函数传递对象参数时,为初始化形参,要调用复制初始化构造函数,在被调用函数返回时,形参的生存期结束,它的析构函数被调用;在函数 返回一个对象时,要使用返回值初始化调用函数内部的一个(隐藏)对象,这时也要调用复制初始化构造函数,在该隐藏对象的生存期结束时,同样,要调用它的析 构函数.

在对象用作函数的参数以及函数返回一个对象这两种情况都被视为对对象的初始化.注意:为一个类定义恰当的初始化过程是保证程序正确工作的基础.

如果一个类定义了复制初始化构造函数,它的对象又常用作函数的参数,这时如果使用引用调用,则比使用值调用的程序有更好的效率,因为引用不是对象,在初始 化引用时不调用复制初始化构造函数(注意:在引用生存期结束时,也不调用析构函数).虽然返回引用可以改善程序的效率,但不能返回对局部对象的引用.

下面的例子能够很好的说明上面的问题.

 

/**
 * @author hqlong
 * @link   http://hqlong.com
 * @date   2007-05-31 明天六一儿童节了
 */
#include <iostream>
using namespace std;

class MyClass{
private:
	int val;
public:
	MyClass(int i=0) {val = i;}
	MyClass(MyClass& cp);
	void set(int i);
	void print();
	~MyClass();
};
MyClass::MyClass(MyClass& cp){
	val = cp.val;
	cout << "Hi.val=" << val << endl;
}

void MyClass::set(int i){
	val = i;
}

void MyClass::print(){
	cout << "This Print val=" << val << endl;
}
MyClass::~MyClass(){
	cout <<"Destructor for val=" << val << endl;
}

MyClass MyFun(MyClass);
void gFun();
int main(){

	gFun();
	cout <<"Exiting main" << endl;
	return 0;
}

void gFun(){
	MyClass my(5),ret;
	cout << "MyClass called..." << endl;
	ret = MyFun(my);
	cout << "In gFun..." << endl;
	cout << "my ==>";
	my.print();
	cout <<"ret ==>";
	ret.print();
	cout << "Exiting gFun" << endl;
}

MyClass MyFun(MyClass obj){
	cout << "In MyFun..." << endl;
	obj.print();
	obj.set(10);
	cout << "Returning..." << endl;
	return obj;
}

程序运行结果如下:

 

MyClass called...
Hi.val=5		//在参数传递中调用了MyClass::MyClass(MyClass&)
In MyFun...
This Print val=5
Returning...
Hi.val=10		//在函数返回时调用了MyClass::MyClass(MyClass&)
Destructor for val=10	//这两个输出是调用参数obj和gFun()中的
Destructor for val=10 	//隐藏对象的析构函数时产生的
In gFun...
my ==>This Print val=5
ret ==>This Print val=10
Exiting gFun
Destructor for val=10	//对象ret的析构函数被调用
Destructor for val=5    //对象my的析构函数被调用
Exiting main

2007-05-31 机器人 于北京

纯虚函数与抽象类(C++学习笔记)

Posted by 机器人 on 31st 五月 2007 in c/c++

在许多情况下,在基类中不能为虚函数给出一个有意义的定义,这时可以将它说明为纯虚函数.它的定义留给派生类来做.说明纯虚函数的一般形式为:

class  类名{

       virtual 类型 函数名( 参数列表 )=0;

}

下面是一个纯虚函数的简单例子:

/**
 * @author hqlong
 * @link   http://hqlong.com
 * @date   2007-05-31
 */
#include <iostream>
using namespace std;
class A{
public:
        virtual void echo() = 0;//纯虚函数,作为类A的抽象类
};
class B:public A{
public:
        virtual void echo(){
               cout << "Class B" << endl;
        }
};
class C: public B{
public:
        virtual void echo(){
               cout << "Class C" << endl;
        }
};
 
void show(A *a){
        a->echo();
}
int main(){
        B *b = new B;// B::echo();
        C *c = new C;// C::echo()
        show(b);
        show(c);
        return 0;
}

在该例子中,A类的虚函数echo()仅起到为派生类提供一个一致的接口作用,派生类中重定义的echo()用来决定显示内容,由于在A类中不能对些作决定,因些被说明为纯虚函数.

1.         一个类可以说明多个纯虚函数,包含有纯虚函数的类称为抽象类.一个抽象类只能作为基类来派生新类.不能说明抽象类的对象.

A a; //error

但可以说明指向抽象类的指针(或引用).

A *p; // OK

2.         从一个抽象类派生的类必须提供纯虚函数的实现代码,或者在该派生类中仍将它说明为纯虚函数.否则编译器将提示错误信息.

3.         说明了纯虚函数的派生类仍是抽象类.如果派生类中给出了基类所有的纯虚函数的实现,则该派生类不再是抽象类.

4.         抽象类至少含有一个虚函数,而且至少有一个虚函数是纯虚函数.

5.         一定要纯虚函数与空的虚函函数区分开来.

virtual void echo() = 0; //纯虚函数
virtual void echo(){}; //空的虚函数

 

6.         在成员函数内可以调用纯虚函数,但在构造函数或者析构函数内调用一个纯虚函数将导致程序运行错误,因为没有纯虚函数定义代码.

class A{
public:
    virtual void f() = 0;
        void g() { f(); }      //OK
        A(){f();}              //Error
}

为了进一步说明它们的使用方法,再给一个例子.

编写一个程序,用于计算正方形、三角形和圆的面积.

/**
 * @author hqlong
 * @link   http://hqlong.com
 * @date   2007-05-31
 */
 
#include <iostream>
using namespace std;
//定义抽象类Shape
class Shape{
public:
        virtual float area() = 0;
};
//求面积
float total(Shape *s[],int n){
        float sum;
        for(int i = 0; i<n; i++) sum += s[i]->area();
        return sum;
}
//正方形类
class Triangle:public Shape{
protected:
        float mH,mW;
public:
        Triangle(float h, float w){
               mH = h;
               mW = w;
        }
        float area(){
               return mH * mW * 0.5;
        }
};
//长方形类
class Rectangle:public Triangle{
public:
        Rectangle(float h, float w):Triangle(h,w){}
        float area() { return mH * mW; }
};
//圆类
class Circle:public Shape{
protected:
        float mRadius;
public:
        Circle(float r){mRadius = r;}
        float area(){
               return mRadius * mRadius * 3.14;
        }
};
 
int main(){
        Shape *s[4];
        s[0] = new Triangle(3.0,4.0);
        s[1] = new Rectangle(2.0,4.0);
        s[2] = new Circle(5.0);
        s[3] = new Circle(8.0);
        float sum = total(s,4);
        cout << sum << endl;
        return 0;
}

程序运行如下:

-1.07374e+008

该例子,Shape类的虚函数area仅起到为派生类提供一个一致的接口的作用,派生类中重定义的area()用于决定以什么样式计算面积.由于Shape类中不能对此作出决定,因此被说明为纯虚函数.

赋值兼容规则使我们将正方形、长方形、圆都视为形状,多态性又保证了函数total在对各种形状求面积之和时,无须关心当前正在计算哪种具体形状的面积.在需要时,函数total可从这些形状的对象那里获得该对象的面积,成员函数area保证了这点.

 

2007-05-31 机器人 于 北京

C/C++中枚举类型(enum)的使用解惑

Posted by 机器人 on 30th 五月 2007 in c/c++

 

枚举类型,顾名思义,“枚”作为量词,作“个”讲,那么枚举,就是一个一个的列举,如果一件事情能够被一个一个的列举,那么它的数量肯定就是有限的,否则是不能被一一列举出来的。所以枚举类型即为能被列举的常量的一个集合。

在生活中,枚举的例子随处可见,比如礼拜几,那么就可以作为一个枚举变量。这个变量所存储的值,是有限的,且,能被我们所列举。再比较说,性别。它也可以作为一个枚举类型,我们知道,性别也就只有“男”或者“女”,它是可以被我们所列举的。它能很直观的表达出我们所定义的事件。

如:定义一个枚举类型的变量,虽然不知道变量具体是什么值,但能知道它可能会有哪些值,这样,这样,就能对程序中所出现的变量的取值有一个很好的估量,从而使程序的编写更加顺利。

枚举类型的定义写结构体的定义相似,其形式为:

   枚举的说明与结构和联合相似, 其形式为:

            enum 枚举名{

               标识符[=整型常数],

               标识符[=整型常数],

              

               标识符[=整型常数],

          } 枚举变量;

    如果枚举没有初始化, 即省掉"=整型常数", 则从第一个标识符开始,  依次

次赋给标识符0, 1, 2, …。但当枚举中的某个成员赋值后, 其后的成员按依次

1的规则确定其值。

例如下列枚举说明后, x1, x2, x3, x4的值分别为0, 1, 2, 3

     enum string{x1, x2, x3, x4}x;

    当定义改变成:

      enum string

      {

          x1,

          x2=0,

          x3=50,

          x4,

      }x;

    x1=0, x2=0, x3=50, x4=51

    注意:

    1. 枚举中每个成员(标识符)结束符是",",  不是";", 最后一个成员可省略

","

    2. 初始化时可以赋负数, 以后的标识符仍依次加1

    3. 枚举变量只能取枚举说明结构中的某个标识符常量。

    例如:

      enum string

      {

          x1=5,

          x2,

          x3,

          x4,

      };

     enum strig x=x3;

此时, 枚举变量x实际上是7

 

4.在外部,我们可以对枚举变量进行赋值,不过,得要进行类型转换。

         如果我们不进行类型轮换,即如下所示进行赋值:

         x = 3;

是不允许是,如果对X进行赋值,只能对3进行类型转换.即:

x = (string)3;

那么这样就对了.

如果给x赋的不是一个整形的数,而是一个字符型的,如:

x = (string)’a’;

那么这时候x的值并不是字符’a’,而是’a’ASCII码,我们知道,在枚举类型中,各常量的值只能是整形的,所以在对上例会自动的将’a’转换成一个整数值.从内存的角度来看来话,其实C/C++中整形和字符型的变量是一样的,它们之间可以互相转换.

 

下面是一个使用枚举类型的例子.(从网上收集得到)

#include <iostream>

 

#include <iostream>
using namespace std;

enum Day {Saturday, Sunday = 0, Monday, Tuesday, Wednesday,
Thursday, Friday}; //Saturday = 0 by default, Sunday = 0 as well
void Prnt (Day day)  // Print whether a day is a 'Weekend' or a "Weekday".
{
	if (day ==0) cout << "Weekend" << endl;
	else cout << "Weekday" << endl;
}

int main(){
	enum Fruit {apple, pear, orange, banana} frt1; // 'frt1' can be declarated here.

	// int apple; // error: redefinition of 'apple'

	typedef enum Fruit ShuiGuo; // In c++, 'enum' can be omitted.

	enum Fruit frt2 = apple; // In c++, 'enum' can be omitted.
	ShuiGuo frt3 = pear; // After type-declaration synonym, 'enum' can not exist here!

	frt1 = (Fruit) 0; // 'frt1' can be assigned with number by explicit cast.

	for (int i = apple; i <= banana; i++)
		switch (i)
		{
		   case apple: cout << "apple" << endl; break;
		   case pear: cout << "pear" << endl; break;
		   case orange: cout << "orange" << endl; break;
		   case banana: cout << "banana" << endl; break;
		   default: break;
		}

	// Print whether a day is a 'Weekend' or a "Weekday".
	Prnt (Saturday);
	Prnt (Sunday);
	Prnt (Monday);
	Prnt (Tuesday);
	Prnt (Wednesday);
	Prnt (Thursday);
	Prnt (Friday);

	return 0;
}

机器人 2007-05-30 于北京

网络程序编写所需基本网络知识介绍

Posted by 机器人 on 27th 五月 2007 in c/c++

什么是协议:

为进行网络中的数据交换(通信)而建立的规则、标准或约定。(=语义+语法+规则)

不同层具有各自不同的协议。

 

网络的状况:

多种通信媒介――有线、无线……

不同种类的设备――通用、专用……

不同的操作系统――UNIXWINDOWS……

不同的应用环境――固定、移动

不同的业务种类――分时、交互、实时……

宝贵的投资和积累――有形、无形……

用户业务的延续性――不允许出现大的跌宕起伏。

它们互相交织,形成了非常复杂的系统应用环境。

网络异质性问题的解决

1.          网络体系结构:就是使这些用不同媒介连接起来的不同设备和网络系统在不同的应用环境下实现互操作性。并满足各种业务需求的一种粘合剂,它营造了一种“生存空间”――任何厂商的任何产品、迟缓任何技术只要遵守这个空间的行为规则,就能够在其中生存并发展。

2.          网络体系结构:解决异质性问题采用的是分层方法――把复杂的网络互联问题划分为若干个较小的、单一的问题,在不同层上予为解决。

就是我们在编程时把问题分解为很多小的模块来解决一样。

ISO/OSI七层参考模型

1.          OSI(Open System Interconnection)参考模型将网络的不同功能划分为7层。

 

 

 

 

 

2.          通信实体对对等层之间不允许直接通信。

3.          各层之间是严格单向依赖。

4.          上层使用下层提供的服务――Service user;

5.          下层向上层提供服务――Service provider

 

对等通信示例

对等层通信的实质

1.          对等层实体之间虚拟通信。

2.          下层向上层提供服务,实际通信在最底层完成。

OSI各层所使用的协议

1.          应用层:远程登录协议Telnet、文件传输协议Ftp、超文本传输协议HTTP、域名服务DNS、简单邮件传输协议SMTP、邮局协议POP3等。

2.          传输层:传输控制协议TCP、用户数据报协议UDP

n          TCP:面向连接的可靠的传输协议。

n          是无连接的,不可靠的传输协议。

既然UDP有这么多缺点,那么我们为什么还要去用它呢?

主要查因为UDP不需要建立连接,而且没有数据确认和重传的机制,所以实时性比较高,在一些实时性要求比较高的场合,我们就可以使用UDP进行通信。比如:电话会议,视频点播等,因为对于这些应用,丢失少量的数据,不会影响我们观看视频。而那么地数据完全性要求比较高场合,我们就可以采用TCP,例如:你在网上下载一个安装程序,如果丢失少数数据,你的安装程序就不可以使用了。

3.          网络层:网际协议IPInternet互联网控制报文协议ICMPInternet组管理协议IGMP

 

数据封装

1.          一台计算机要发送数据到另一台计算机,数据首先必须打包,打包的过程称为封装。

2.          封装就是在数据前面加上特定的协议头部。

3.          OSI参考模板中,对等层协议之间交换的信息单元统称为协议数据单元(PDUProtocol Data Unit)。

4.          OSI参考模型中每一层都要依靠下一层提供的服务。

5.          为了提供服务,下层把上层的PDU作为本层的数据封装,然后加入本层头部(和尾部)。头部中含有完成数据传输所需的控制信息。

6.          这样,数据自上而下递交的过程实际上就是不断封装的过程。到达目的地后自下而上递交的过程就是不断拆封的过程。由此可知,在物理线路上传输的数据,其外面实际上被包封了多层“信封”。

7.          但是,某一层只能识别由对等层封装的“信封”,而对于被封装在“信封”内部的数据仅仅是拆封后将其提交给上层,本层不作任何处理。

 

TCP/IP模型

1.          TCP/IP起源于美国国防部高级研究规划署(DARPA)的一项基础研究计划――实现若干台主机的通信。

2.          现在TCP/IP已成为Internet上通信的工业标准。

3.          TCP/IP模型包括4个层次:   

n          应用层

n          传输层

n          网络层

n          网络接口层

 

TCP/IPOSI参考模型的对应关系

端口:

1.          按照OSI七层模型的描述,传输层提供进程(应用程序)通信的能力。为了标识通信实体中进行通信的进程(应用程序),TCP/IP协议提出了协议端口(protocol port,简称端口)的概念。

2.          端口是一种抽象的软件结构(包括一些数据结构和I/0缓冲区)。应用程序通过系统调用与某端口建立连接(binding)后,传输层付给该端口的数据都被相应的进程所接收,相应进程发给传输层的数据都通过该端口输出。

3.          端口用一个整数型标识符来表示,即端口号。端口号跟协议相关,TCP/IP传输层的两个协议TCPUDP是完全独立的两个软件模块,因此各自的端口号也相互独立。

4.          端口使用一个16位数字来表示,它的范围是0655351024以下的端口号保留给预定义的服务。例如:http:使用80商品。

套接字(socket)的引入

1.          为了能够方便的开发网络应用软件,由美国伯克利大学在UNIX上推出了一种应用程序访问通信协议的操作系统调用socket(套接字)。Socket的出现,使程序员可以很方便的访问TCP/IP,从而开发各种网络应用的程序。

2.          随着UNIX的应用推广,套接字在编写网络软件中得到了极大的普及。后来,套接字又被引进了WINDOWS等操作系统,成为开发网络应用程序的非常有效快捷的工具。

3.          套接字存在于通信区域中。通信区域也叫地址族,它是一个抽象的概念,主要用于将通过套接字通信的进程的共有特性综合在一起。套接字通常只与同一区域的套接字交换数据(也有可能跨区域通信,但这只在执行了某种转换进程后才能实现)。WINDOWS SOCKET只支持一个通信区域:网际域(AF_INET),这个域被使用网际协议簇通信的进程使用。

 

网络字节顺序

不同的计算机存放多字节值的顺序不同,有的机器在起始地址存放低位字节(低位先存),有的机器在起始地址存放高位字节(高位先存)。基于INTERCPU,即我们常用的PC机采用的是低位先存。为保证数据的正确性,在网络协议中需要指定网络字节顺序。TCP/IP协议使用16位整数和32位整数的高位先存格式。

客户机/服务器模式

1.          TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户机/服务器模式(client/server),即客户机向服务器提出请求,服务器接收到请求后,提供相应的服务。

2.          客户机/服 务器模式的建立基于以下两点:首先,建立网络的起因是网络中软硬件资源、运算能力和信息不均等,需要共享,从而造就拥有众多资源的主机提供服务,资源较少 的客户请求服务这一非对等作用。其次,网间进程通信完全是异步的,相互通信的进程音即不存在父子关系,又不共享内在缓冲区,因此需要一种机制为希望通信的 进程间建立联系,为二者的数据交换提供同步,这就是基于客户机/服务器模式的TCP/IP

3.          客户机/服务器模式在操作过程中采取的是主动请求的方式。

首先服务器方要先启动,并根据请求提供相应的服务:

1.          打开一个通信通道并告知本地主机,它愿意在某一地址和端口上接收客户请求。

2.          等待客户请求到达该端口。

3.          接收到重复服务请求,处理该表示并发送应答信号。接收到并发服务请求,要激活一个新的进程(或线程)来处理这个客户请求。新进程(或线程)处理此客户请求,并不需要对其它请求作出应答。服务完成后,关闭新进程与客户的通信链路,并终止。

4.          返回第二步,等待另一客户请求。

5.          关闭服务器。

客户方:

1.          打开一个通信通道,并连接到服务器所在主机的特定端口。

2.          向服务器发服务请求报文,等待并接收应答;断续提出请求。

3.          请求结束后关闭通信通道并终止。

套接字类型

1.          流式套接字(SOCK_STREAM

提供面向连接、可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接收。(tcp

2.          数据报式套接字(SOCK_DGRAM

提供无连接服务。数据包以独立包形式发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。

3.          原始套接字(SOCK_RAW

基于TCP(面向连接)的socket编程

1.          服务器端程序:

1.          创建套接字(socket)。

2.          将套接字绑定到一个本地地址和端口上(bind)。

3.          将套接字设为监听模式,准备接收客户请求(listen)。

4.          等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。

5.          用返回的套接字和客户端进行通信(send/recv)

6.          返回,等待另一客户请求。

7.          关闭套接字。

2.          客户端程序:

1.          创建套接字(socket)。

2.          向服务器发出连接请求(connect)

3.          和服务器端进行通信(send/recv)。

4.          关闭套接字。

基干UDP(面向无连接)的socket编程

3.          服务器(接收端)程序:

1.          创建套接字(socket)。

2.          将套接字绑定到一个本地地址和端口上(bind)

3.          等待接收数据(recvfrom)。

4.          关闭套接字。

4.          客户端(发送端)程序:

1.          创建套接字(socket)。

2.          向服务器发送数据(sendto.

3.          关闭套接字。

 

2007-05-27机器人 于 北京

 

滚动控件及其范例(VC++学习笔记)

Posted by 机器人 on 27th 五月 2007 in c/c++

设置滚动控件范围附加初始化工作。

Code Section:

BOOL CScrollDlg::OnInitDialog()
{
       //omit
    CScrollBar *pScroll=(CScrollBar*)GetDlgItem(IDC_SCROLL);
      pScroll->SetScrollRange(-100,100);
      pScroll->SetScrollPos(0);
      SetDlgItemInt(IDC_CURPOS,0);
      return TRUE;  // return TRUE  unless you set the focus to a control
}

 

滚动事件处理信息:

Code Section

void CScrollDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	// TODO: Add your message handler code here and/or call default
	//获得原有的流动条位置
	int iPos = pScrollBar->GetScrollPos();
	/*CString str;
	str.Format("%d",iPos);
	MessageBox(str);*/
	//根据不同的拖动方式设置新的滚动位置

	switch(nSBCode){
	//向右滚动一行
	case SB_LINERIGHT:
		iPos+=1;
		break;
	//向左滚动一行
	case SB_LINELEFT:
		iPos-=1;
		break;
	//向右滚动一页
	case SB_PAGERIGHT:
		iPos+=10;
		break;
	//向左滚动一页
	case SB_PAGELEFT:
		iPos-=10;
		break;
	//直接拖动滚动块
	case SB_THUMBTRACK:
		iPos=nPos;
		break;
	default:
		break;
	}
	//滚动条的最大位置不超过100,最小位置不小于-100;
	if(iPos<-100) iPos=-100;
	if(iPos>100) iPos = 100;
	//必须手动更新滚动条的当前位置
	pScrollBar->SetScrollPos(iPos);
	//显示滚动条的当前位置
	SetDlgItemInt(IDC_CURPOS,iPos);
	CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}

根据文本框的输入改变滚动条的位置。

Code Section:

void CScrollDlg::OnChangeCurpos()
{
	// TODO: If this is a RICHEDIT control, the control will not
	// send this notification unless you override the CDialog::OnInitDialog()
	// function and call CRichEditCtrl().SetEventMask()
	// with the ENM_CHANGE flag ORed into the mask.

	// TODO: Add your control notification handler code here
	CString str;
	GetDlgItemText(IDC_CURPOS,str);
	str.TrimLeft();
	str.TrimRight();
	int iPos = 0;
	if(str != "-1" && str != ""){
		if(!UpdateData()){
			return;
		}
		iPos = m_iCurPos;
	}
	CScrollBar *pScroll=(CScrollBar*)GetDlgItem(IDC_SCROLL);
	pScroll->SetScrollPos(iPos);

}

VC下cannot open file "mfc42u.lib" 出错的解决方法

Posted by 机器人 on 26th 五月 2007 in c/c++

出错信息: fatal error LNK1104: cannot open file "mfc42u.lib"""

缺少VC的unicode支持库.

在vc安装盘中找到MFC42U.LIB , MFCS42U.LIB,把其复制到

C:\Program Files\Microsoft Visual Studio\VC98\MFC\Lib

重编译即可.

动态创建按钮控件及其常用函数介绍(VC++)

Posted by 机器人 on 25th 五月 2007 in c/c++

消息定义原型如下:

XXXDlg.h中定义:

 

// Implementation
protected:
	HICON m_hIcon;

	// Generated message map functions
	//{{AFX_MSG(CPushButtonDlg)
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	afx_msg void OnPushme();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()

 

消息映射入口。它位于实现文件中XXXDlg.cpp;代码片断如下:

 

BEGIN_MESSAGE_MAP(CPushButtonDlg, CDialog)
	//{{AFX_MSG_MAP(CPushButtonDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_PUSHME, OnPushme)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

 

其中第一个参数IDC_PUSHME为控件的标识符,第二个参数OnPushmeo为相应的消息处理函数。

 

常用成员操作函数。

l         GetState:获取按钮的选择状态,高亮状态以及焦点状态。函数原型为

UNIT GetState() const;

例:如下这段代码创建按钮并反转其高亮状态。

m_myButton = newCButton;

       //创建下压按钮

       m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_PUSHBUTTON,

       CRect(10,10,100,30),this,1);

       //反转其高亮状态

       m_myButton->SetState(!(m_myButton->GetState() && 0x0004));

l         SetState:设置按钮的高亮状态。函数原型为

void SetState(BOOL,bHightlight);

例:如下代码创建按钮并设置其状态为按下。

 

m_myButton = newCButton;
	//创建下压按钮
	m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
	CRect(10,10,100,30),this,1);
	//设置其状态为按下
	m_myButton->SetState(!(m_myButton->GetState() && 0x0004));

 

l         GetCheck():获取按钮的选中状态。函数原型为

int GetCheck() const;

例:如下这段代码创建按钮关设置其选中状态为三态的下一个状态。

m_myButton = new CButton;

	//创建三态按钮
	m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_AUTO3STATE,
	CRect(10,10,100,30),this,1);
	//设置其选中状态为三态的下一个状态
	//(即0到1, 1 到2, 2 到0).
	m_myButton->SetCheck(m_myButton->GetCheck() % 3);

 

l         SetCheck:设置按钮的选中状态。函数原型为

void SetCheck( int nCheck );

例:如下这段代码创建一三态按钮并设置其选中状态为中间态。

m_myButton = new CButton;

	//创建三态按钮
	m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_AUTO3STATE,
	CRect(10,10,100,30),this,1);
	//设置其选中状态为中间态
	m_myButton->SetCheck(2);

l         GetButtonStyle:获取按钮样式信息。函数原型为

UINT GetButtonStyle() const;

例:如下这段代码创建一三态按钮并使用其中一种“自动”样式设置按钮,如把一般下压按钮变为默认下压按钮。

m_myButton = new CButton;

	//创建三态按钮
	m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_AUTO3STATE,
	CRect(10,10,100,30),this,1);
	//使用其中一种“自动”样式设置按钮,如把一般下压按钮变为默认下压钮
	UINT uStyle = m_myButton->GetButtonStyle();
	if ( uStyle & BS_PUSHBUTTON )
		uStyle = (uStyle & ~ BS_PUSHBUTTON) | BS_DEFPUSHBUTTON;
	else if (uStyle & BS_RADIOBUTTON)
		uStyle = (uStyle & ~ BS_RADIOBUTTON) | BS_AUTORADIOBUTTON;
	else if (uStyle & BS_CHECKBOX)
		uStyle = (uStyle & ~ BS_CHECKBOX) | BS_AUTOCHECKBOX;
	else if (uStyle & BS_3STATE)
		uStyle = (uStyle & ~ BS_3STATE) | BS_AUTO3STATE;
	m_myButton->SetButtonStyle(uStyle);

l         SetButtonStyle:改变按钮样式。函数原型为

void SetButtonStyle(UINT nStyle,BOOL bRedraw=TRUE);

例如:如下代码创建三态按键样式并改变按钮样式为自动复选框。

m_myButton = new CButton;
	//创建三态按钮
	m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_AUTO3STATE,
	CRect(10,10,100,30),this,1);
	//改变按钮样式为自动复选框
	m_myButton->SetButtonStyle(BS_AUTOCHECKBOX);

l         GetIcon:获取用SetIcon()设置的图标句柄。函数原型为:

HICON GetIcon()const;

例:如下代码创建一显示图标按钮。

m_myButton = new CButton;
	//创建可显示图标的按钮
	m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_ICON,
	CRect(10,10,100,30),this,1);
	//如果按钮没有定义图标,用系统的报错图标定义之
	if(m_myButton->GetIcon == NULL)
	m_myButton->SetIcon(::LoadIcon(NULL,IDI_ERROR));

l         SetIcon:设置一图标,将其显示在按钮上。函数原型为:

HICON SetIcon(HICON hIcon);

例:如下代码创建一可显示图标按钮并将图标设置为系统询问图标。

 

m_myButton = new CButton;
	//创建可显示图标的按钮
	m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_ICON,
	CRect(10,10,100,30),this,1);
	//如果按钮没有定义图标,用系统的报错图标定义之
	if(m_myButton->GetIcon == NULL)
	//设置图标按钮图标为系统询问图标
	m_myButton->SetIcon(::LoadIcon(NULL,IDI_QUESTION));

l         GetBitMap:获取先前用SetBitMap设置的按钮位图句柄。函数原型为:

HBITMAP GetBitMap()const;

例:如下代码创建一可显示位图按钮并显示系统关闭位图。

m_myButton = new CButton;
	//创建可显示位图的按钮
	m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_BITMAP,
	CRect(10,10,100,30),this,1);
	//在按钮上显示系统关闭位图
	if(m_myButton->GetIcon == NULL)
	if(m_myButton->GetBitmap() == NULL)
	m_myButton->SetBitmap(::LoadBitmap(NULL,MAKEINTRESOURCE(OBM_CLOSE)));

l         SetBitMap:设置按钮上显示的位图。函数原型为:

HBITMAP SetBitMap(HBITMAP hBitmap);

例如:如下代码创建一可显示位图按钮并在其上显示系统的复选按钮标记位图。

m_myButton = new CButton;
	//创建可显示位图的按钮
	m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_BITMAP,
	CRect(10,10,100,30),this,1);
	//在按钮上显示系统的复选按钮标记位图
	if(m_myButton->GetIcon == NULL)
	m_myButton->SetBitmap(::LoadBitmap(NULL,MAKEINTRESOURCE(OBM_CHECK)));

l         GetCursor:获取先前用SetCursor设置的光标句柄。函数原型为:

HCURSOR GetCursor();

例:如下代码创建一可显示图标按钮并在设置关联光标为系统帮助光标。

m_myButton = new CButton;
	//创建可显示图标的按钮
	m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_BITMAP,
	CRect(10,10,100,30),this,1);
	//设置关联光标为系统帮助光标
	if(m_myButton->GetCursor() == NULL)
	m_myButton->SetCursor(::LoadCursor(NULL,IDC_HELP));

l         SetCursor:为按钮设置相关联的光标。函数原型为:

HCURSOR SetCursor( HCURSOR hCursor );

例如下代码创建一可显示图标按钮并在设置关联光标为程序启动时的箭头加漏斗光标。

m_myButton = new CButton;
	//创建可显示图标的按钮
	m_myButton->Create(_T("Mybutton"), S_CHILD|WS_VISIBLE|BS_BITMAP,
	CRect(10,10,100,30),this,1);
	//设置关联光标为程序启动时的箭头加漏斗光标
	m_myButton->SetCursor(::LoadCursor(NULL,IDC_APPSTARTING));
 2007-5-25 机器人 于 北京

 

用户永久登录讲解(小兰发言)

Posted by 机器人 on 24th 五月 2007 in phpoo

19:21 2007-5-24

小兰发言了:

关于用户永久登录的一些历史背景……,引入了QQ登录,随后就来了CS VS BS的较量。

以前还以为永久登录只设置一个COOKIE,昨晚小黑一讲,原来对此理解大错特错了。。。。,在SEESION禁用的情况下,COOKIE为什么可以用呢?

杨煌插一句。。”在使用SESSION的时候,如果客户端的COOKIE没有被本地禁用的话,那么SESSION是先保存在本地COOKIE里的,但如果被禁用的话,那么就会保存到隐藏域里或者放在URL后的串里。。“。

首先讲讲COOKIE不安全一面。当我们第一次访问一个站点的时候,如果该站点有COOKIE,那么就会向本地注册一个COOKIE变量,那么我们下次再对该网站进行访问的时候,就可以访问该COOKIE变量,但是,如果在本地(客户端)对COOKIE的值(保存在本地的一个文件)进行更改,那么服务器是无法识别它是否是真实的值,即,接触该电脑的人都可能对该值进行串改,所以从这方面来讲,COOKIE是很不安全的,所以我们对敏感数据如果还是按照传统的方式对其进行保存的话,那么后果的严重性大家可想而知。

 讲到安全,杨煌给大家讲了讲关于加密方面的知识,就拿了SSL(安全套字层)的概念,一些简单的加密算法,来加强大家对加密这方面的印象。

小兰继续。我平时看了此网上的资料,都没有意识到这方面的东西,这方面的资料很少发现。首先在数据库里放一个字段,当然在对COOKIE的存储的时候,不可能把用户名和密码这些比较第三的数据放在里面,还加了过期时间等附加信息。对COOKIE做永久登录,我们做成一次验证,为了实现这样的效果,要让它在尽量少的时间时使其过期。。当数据库里多了这个标识后,当我们在用 个标识,这个标识符(使用随机数来生成的《为了避免重复,可以使它过期,当然使用时间戳,就可以避免这一点》)入库(用户名加随机串入库),别一标识符存放过期时间,然后用一个COOKIE变量将其进行保存,如果用户名随便串再加过期时间组合在一起,我们这样的重复性就比较小了。如果有效期输入登录的话,那么我们就不用输入用户名。

在这里所讲的都是假定一个固定IP,对于多IP的情况,我们在这里先不考虑。

如果我们下次登录的,就先用本地的COOKIE变量去和服务器里保存的变量进行对比,如果一样,我们就不用再登录,否则不能登录,如果发再我们保存的数据过期的话,那么就输入用户名,对有效期再一次进行注册。

来一个总述吧。

通过浏览器里的这个COOKIE到数据库里去找这个用户,如果这个用户所对应的用户有的吗?那么我就可以证明你已经登录了。这样就可以简化了用户了登录,而且用户数据是通过本地的COOKIE变量去库里查询的,所以数据就相对来讲,就安全多了。如果在有效期内进行登录,那么我们就不用输入用户名和密码,如果已经超过有有效期,那么就再次登录,重新注册COOKIE本地变量,那么和当前用户有关的数据在本地不可能得到,必需通过本地变量才能从库里得到和用户相关的数据,如果向我们直接把用户数据入在本地,那么很可以黑客就通过一些手段,或者是使用当前电脑人就很容易得到本地用户的数据。但是,如果我们只存入用户名,那么本地用户只能得到对它来讲毫无意义的一个数据,但这个数据对于服务器来说,它就可以得到用户相关的数据,但是这此本地COOKIE变量不通过我们的程序,如果不知道其中的规则,那么也是不可能得到用户数据的,所以只通过本地的COOKIE查询登录后用户需要用的相关数据,所以这样就可以大大提高数据对安全性。

20:35 2007-5-24

小兰

指向常量的指针和指针常量的区别

Posted by 机器人 on 20th 五月 2007 in c/c++

指向常量的指针和指针常量

1. 指向常量的指针

char ch[5] = “lisi”;

我们先定义了一个字符数组,它有五个元素,我们用一个常量的字符串对它进行了赋值,要注意的是,这种赋值形式只能在数组定义的同时进行,为什么这里我们定义的5个元素则不是4个元素的字符数组呢?要注意,对于常量字符串来说,会自动在末尾加上一个”\0”,所以在这个地方我们定义的是5而不是4。

我们假定0088::4400是字符数组ch在内存中分配的首地址,接下来我们用

const char * pStr = ch;

定义了一个指向常量的指针变量。要注意在这里,const在char前面,其实const在char的前面或者在char的后面都一样,不过一般我们把它定义在char的前面。用const char *去定义一个指向常量的指针变量,然后我们用这个字符数组给这个这个指向常量的指针变量赋值。我们知道数组名代表了数组的首地址,那么上面的操作就相当于把ch这个字符数组的首地址赋给了这个指针变量。那么我们所定义的pStr是指向常量的指针变量,也就是说我们不可以用pStr去修改它在内存当中的这个数据的内容,但是对于pStr本身的值,也就是地址值这个是可以修改的。表示常量的指针表示它所指向的对象是常量。我们再看下面的代码。

*pStr = ‘w’; //错误

pStr = ‘hqlong’;//正确

第一句我们想利用*pStr去修改第一个字节所指向的内容,即,我们把这个指针的常量的第一个字节的内容设为’w’,那么这是错误的,因为我们通过上面知道,这是一个指向常量的指针变量,也就是这个指针变量所指地址的内容不能被修改,我们知道,常量是不能被修改的,所以在这里,这样的赋值是错误的。

第二个表达式,我们是用一个字符串对这个变量赋值,这个操作就相当于把这个字符串的地址赋给了这个变量。pStr它的内容,也就是这个指针值,或者地址是可以修改的,所以这个操作是允许的,是正确的。

要注意,虽然我们不能通过pStr去修改它所指向的内存的内容,对于上面的字符数组来说,我们仍然可以去利用字符数组去改变内存当中的数据的内容,所以在注意这点的区别。

我们在申明的时候,就申明了pStr是指向常量的指针,那么就保证要我们在编译的时候,就不能通过pStr去修改它在内存中所指的内容。我们在申明一个函数的时候,用指针来传参,我们通常是把这个行参申明了指向常量的指针类型,这样当实参传进来之后,行参的类型是指向常量的指针类型,所以我们不能够利用行参去修改它所指向的内容,从而保证了数据的一致性。

2. 指针常量

同样们也定义了字符数组:

char ch[5] = “list”;

char * const pStr = ch;

注意第二行,和前面的定义指向常量的指针的区别,const的位置是在*和pStr之间,而指向常量的指针则是在char前面或者紧跟其后。同样我们用字符数组给这个指针赋值,即把这个字符数组的首地址赋给了这个指针。大家要注意,指针常量必须是我们定义的同时对它进行赋值,也就是说,我们不能先定义,过后才对其进行赋值。然后对于前面的指向常量的指针,我们则可以先定义好了,在下一行再对它进行赋值。所以一定要注意这里的区别。

指针常量它表示指针本身是常量,也就是说对这个指针值是不可以修改,但指针所指内容我们是可以修改的,这和指向常量的指针正好相反。

还是举一下上面的两个例子:

pStr = ‘hqlong’;//错误

*pStr = ‘w’; //正确

第一句代码,我们把一个字符串赋给这个指针变量,也就是把这个字符串的首地址赋给这个字符变量,由于指针常量的地址是不可以修改的,所以在这里给其地址进行修改是错误的,在我们编译里,会出错。

第二句代码是想修改pStr所指内存的第一个字节的内容,我们想把它修为’w’,这个操作是正确的,我们知道,指针常量的地址不可以被修改,但地址所指的内容是可以被修改的。

所以在这里,大家要注意指向常量的指针和指针常量的区别,指向常量的指针所在内存地址中的内容我们不可以修改,因为它所指内容是常量。但我们可以修改它的地址,即可以通过修改它的地址值来修改它的值。而指针常量则正好相反,这个指针是常量,指针也就是地址,所以地址是个常量,所以我们不能对其地址进行修改,但可以对该地址内的值进行修改,所以大家一样要注意。

上面的是我一直以后比较迷惑的地方,现在基本上明白是怎么回事,希望能通过这篇文章使不清楚指向常量的指针和指针常量的朋友能够理解这两者的区别。

机器人 5/20/2007 10:38:21 PM 于北京

(转载)改变人生的28句励志名言

Posted by 机器人 on 20th 五月 2007 in mylife

1、大多数人想要改造这个世界,但却罕有人想改造自己。

2、积极的人在每一次忧患中都看到一个机会, 而消极的人则在每个机会都看到某种忧患。

3、莫找借口失败,只找理由成功。(不为失败找理由,要为成功找方法)

4、伟人之所以伟大,是因为他与别人共处逆境时,别人失去了信心,他却下决心实现自己的目标。

5、世上没有绝望的处境,只有对处境绝望的人。

6、当你感到悲哀痛苦时,最好是去学些什么东西。学习会使你永远立于不败之地。

7、世界上那些最容易的事情中,拖延时间最不费力。

8、人之所以能,是相信能。

9、一个有信念者所开发出的力量,大于99个只有兴趣者。

10、每一发奋努力的背后,必有加倍的赏赐。

11、人生伟业的建立 ,不在能知,乃在能行。

12、任何的限制,都是从自己的内心开始的。

13、含泪播种的人一定能含笑收获。

14、欲望以提升热忱,毅力以磨平高山。

15、一个能从别人的观念来看事情,能了解别人心灵活动的人永远不必为自己的前途担心。

16、一个人最大的破产是绝望,最大的资产是希望。

17、不要等待机会,而要创造机会。

18、如果寒暄只是打个招呼就了事的话,那与猴子的呼叫声有什么不同呢? 事实上,正确的寒暄必须在短短一句话中明显地表露出你对他的关怀。

19、昨晚多几分钟的准备,今天少几小时的麻烦。

20、做对的事情比把事情做对重要。

21、人格的完善是本,财富的确立是末。

22、没有一种不通过蔑视、忍受和奋斗就可以征服的命运。

23、行动是治愈恐惧的良药,而犹豫、拖延将不断滋养恐惧。

24、没有天生的信心,只有不断培养的信心。

25、只有一条路不能选择——那就是放弃的路;只有一条路不能拒绝——那就是成长的路。

26、人性最可怜的就是:我们总是梦想着天边的一座奇妙的玫瑰园,而不去欣赏今天就开在我们窗口的玫瑰。

27、征服畏惧、建立自信的最快最确实的方法,就是去做你害怕的事,直到你获得成功的经验。

28、失败是什么?没有什么,只是更走近成功一步;成功是什么?就是走过了所有通向失败的路,只剩下一条路,那就是成功的路。