flash

posted @ 2010-06-25 17:43 莫失莫忘 阅读(82) | 评论 (0)编辑 收藏

第一章 基础

初始化

SDL由八个子系统组成——音频、CDROM、事件处理、文件I/O、游戏杆、线程、记时器和视频。使用前必须调用SDL_Init或 SDL_InitSubSystem初始化。SDL_Init必须早于其他所有SDL调用,它将自动初始化事件处理、文件I/O和线程子系统,并根据参数 选择启动其他子系统。例如,初始化缺省和视频子系统:

SDL_Init(SDL_INIT_VIDEO);初始化缺省、视频和记时器子系统:

SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);SDL_Init对应SDL_Quit(和SDL_QuitSubSystem)。SDL_Quit关闭所有子系统,必须在程序关闭前调用。

除此之外,我们还必须进行错误处理。很多SDL函数返回一个值指示成功与否。例如SDL_Init失败时返回-1。每当SDL出错时,错误信息被保存,并可用SDL_GetError取得。

例1-1 初始化SDL

#include "SDL.h" /* All SDL App's need this */

#include <stdio.h>

int main() {

printf("Initializing SDL.\n");

/* Initialize defaults, Video and Audio */

if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)==-1)) {

printf("Could not initialize SDL: %s.\n", SDL_GetError());

exit(-1);

}

printf("SDL initialized.\n");

printf("Quiting SDL.\n");

/* Shutdown all subsystems */

SDL_Quit();

printf("Quiting....\n");

exit(0);

}

第二章 图像和视频

SDL Vidow显示

初始化SDL Video显示

视频是最常用的部分,也是SDL最完整的子系统。下面的初始化过程是每个SDL程序都要做的,即使可能有些不同。

例2-1 初始化视频

SDL_Surface *screen;

/* Initialize the SDL library */

if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {

fprintf(stderr,

"Couldn't initialize SDL: %s\n", SDL_GetError());

exit(1);

}

/* Clean up on exit */

atexit(SDL_Quit);

/*

* Initialize the display in a 640x480 8-bit palettized mode,

* requesting a software surface

*/

screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);

if ( screen == NULL ) {

fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",

SDL_GetError());

exit(1);

}

初始化最佳视频模式

如果你希望某种色深(颜色数)但如果用户的显示器不支持也可以接受其他色深,使用加SDL_ANYFORMAT参数的SDL_SetVideoMode。您还可以用SDL_VideoModeOK来找到与请求模式最接近的模式。

例2-2 初始化最佳视频模式

/* Have a preference for 8-bit, but accept any depth */

screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE|SDL_ANYFORMAT);

if ( screen == NULL ) {

fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",

SDL_GetError());

exit(1);

}

printf("Set 640x480 at %d bits-per-pixel mode\n",

screen->format->BitsPerPixel);

读取并显示BMP文件

当SDL已经初始化,视频模式已经选择,下面的函数将读取并显示指定的BMP文件。

例2-3 读取并显示BMP文件

void display_bmp(char *file_name)

{

SDL_Surface *image;

/* Load the BMP file into a surface */

image = SDL_LoadBMP(file_name);

if (image == NULL) {

fprintf(stderr, "Couldn't load %s: %s\n", file_name, SDL_GetError());

return;

}

/*

* Palettized screen modes will have a default palette (a standard

* 8*8*4 colour cube), but if the image is palettized as well we can

* use that palette for a nicer colour matching

*/

if (image->format->palette && screen->format->palette) {

SDL_SetColors(screen, image->format->palette->colors, 0,

image->format->palette->ncolors);

}

/* Blit onto the screen surface */

if(SDL_BlitSurface(image, NULL, screen, NULL) < 0)

fprintf(stderr, "BlitSurface error: %s\n", SDL_GetError());

SDL_UpdateRect(screen, 0, 0, image->w, image->h);

/* Free the allocated BMP surface */

SDL_FreeSurface(image);

}

直接在显示上绘图

下面两个函数实现在图像平面的像素读写。它们被细心设计成可以用于所有色深。记住在使用前要先锁定图像平面,之后要解锁。

在像素值和其红、绿、蓝值间转换,使用SDL_GetRGB()和SDL_MapRGB()。

例2-4 getpixel()

/*

* Return the pixel value at (x, y)

* NOTE: The surface must be locked before calling this!

*/

Uint32 getpixel(SDL_Surface *surface, int x, int y)

{

int bpp = surface->format->BytesPerPixel;

/* Here p is the address to the pixel we want to retrieve */

Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

switch(bpp) {

case 1:

return *p;

case 2:

return *(Uint16 *)p;

case 3:

if(SDL_BYTEORDER == SDL_BIG_ENDIAN)

return p[0] << 16 | p[1] << 8 | p[2];

else

return p[0] | p[1] << 8 | p[2] << 16;

case 4:

return *(Uint32 *)p;

default:

return 0; /* shouldn't happen, but avoids warnings */

}

}

例2-5 putpixel()

/*

* Set the pixel at (x, y) to the given value

* NOTE: The surface must be locked before calling this!

*/

void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)

{

int bpp = surface->format->BytesPerPixel;

/* Here p is the address to the pixel we want to set */

Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

switch(bpp) {

case 1:

*p = pixel;

break;

case 2:

*(Uint16 *)p = pixel;

break;

case 3:

if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {

p[0] = (pixel >> 16) & 0xff;

p[1] = (pixel >> 8) & 0xff;

p[2] = pixel & 0xff;

} else {

p[0] = pixel & 0xff;

p[1] = (pixel >> 8) & 0xff;

p[2] = (pixel >> 16) & 0xff;

}

break;

case 4:

*(Uint32 *)p = pixel;

break;

}

}

例2-6 使用上面的putpixel()在屏幕中心画一个黄点

/* Code to set a yellow pixel at the center of the screen */

int x, y;

Uint32 yellow;

/* Map the color yellow to this display (R=0xff, G=0xFF, B=0x00)

Note: If the display is palettized, you must set the palette first.

*/

yellow = SDL_MapRGB(screen->format, 0xff, 0xff, 0x00);

x = screen->w / 2;

y = screen->h / 2;

/* Lock the screen for direct access to the pixels */

if ( SDL_MUSTLOCK(screen) ) {

if ( SDL_LockSurface(screen) < 0 ) {

fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());

return;

}

}

putpixel(screen, x, y, yellow);

if ( SDL_MUSTLOCK(screen) ) {

SDL_UnlockSurface(screen);

}

/* Update just the part of the display that we've changed */

SDL_UpdateRect(screen, x, y, 1, 1);

return;


并用SDL和OpenGL

SDL可以在多种平台(Linux/X11, Win32, BeOS, MacOS Classic/Toolbox, MacOS X, FreeBSD/X11 and Solaris/X11)上创建和使用OpenGL上下文。这允许你在OpenGL程序中使用SDL的音频、事件、线程和记时器,而这些通常是GLUT的任务。

初始化

和普通的初始化类似,但有三点不同:必须传SDL_OPENGL参数给SDL_SetVideoMode;必须使用SDL_GL_SetAttribute指定一些GL属性(深度缓冲区位宽,帧缓冲位宽等);如果您想使用双缓冲,必须作为GL属性指定

例2-7 初始化SDL加OpenGL

/* Information about the current video settings. */

const SDL_VideoInfo* info = NULL;

/* Dimensions of our window. */

int width = 0;

int height = 0;

/* Color depth in bits of our window. */

int bpp = 0;

/* Flags we will pass into SDL_SetVideoMode. */

int flags = 0;

/* First, initialize SDL's video subsystem. */

if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {

/* Failed, exit. */

fprintf( stderr, "Video initialization failed: %s\n",

SDL_GetError( ) );

quit_tutorial( 1 );

}

/* Let's get some video information. */

info = SDL_GetVideoInfo( );

if( !info ) {

/* This should probably never happen. */

fprintf( stderr, "Video query failed: %s\n",

SDL_GetError( ) );

quit_tutorial( 1 );

}

/*

* Set our width/height to 640/480 (you would

* of course let the user decide this in a normal

* app). We get the bpp we will request from

* the display. On X11, VidMode can't change

* resolution, so this is probably being overly

* safe. Under Win32, ChangeDisplaySettings

* can change the bpp.

*/

width = 640;

height = 480;

bpp = info->vfmt->BitsPerPixel;

/*

* Now, we want to setup our requested

* window attributes for our OpenGL window.

* We want *at least* 5 bits of red, green

* and blue. We also want at least a 16-bit

* depth buffer.

*

* The last thing we do is request a double

* buffered window. '1' turns on double

* buffering, '0' turns it off.

*

* Note that we do not use SDL_DOUBLEBUF in

* the flags to SDL_SetVideoMode. That does

* not affect the GL attribute state, only

* the standard 2D blitting setup.

*/

SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );

SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );

SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );

SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );

SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

/*

* We want to request that SDL provide us

* with an OpenGL window, in a fullscreen

* video mode.

*

* EXERCISE:

* Make starting windowed an option, and

* handle the resize events properly with

* glViewport.

*/

flags = SDL_OPENGL | SDL_FULLSCREEN;

/*

* Set the video mode

*/

if( SDL_SetVideoMode( width, height, bpp, flags ) == 0 ) {

/*

* This could happen for a variety of reasons,

* including DISPLAY not being set, the specified

* resolution not being available, etc.

*/

fprintf( stderr, "Video mode set failed: %s\n",

SDL_GetError( ) );

quit_tutorial( 1 );

}

posted @ 2010-06-11 17:00 莫失莫忘 阅读(483) | 评论 (0)编辑 收藏

  显然DirectDraw是Windows下写2D图形程序的最好选择,虽然Direct3D也可以写,但是没DirectDraw简单方便,特别对于初学者,一来就接触那么多函数和参数总不是件愉快的事,所以我的文章主要结合我做的工作,谈谈DirectDraw编程中一些比较关键的技术,大多是我自己想出来的。我想先声明,我的文章可以任意转载,源代码可以任意使用和修改。

  由于我是业余时间写的文章,所以只能每次发表一篇,希望我的工作可以为大家的游戏增光添彩,同时我的文章主要面向有基本C++,DirectDraw,汇编和MMX编程经验的朋友,如果你对这些了解不够,请先学习一下再阅读。也欢迎大家和我交流,我的QQ是35830152,EMAIL:EUHO@SINA.COM。

  作为第一篇,我想先谈谈Alpha混合的问题。这里32位色的图形模式我们不考虑,因为技巧并不多,占用显存和内存大,实际应用的也不多。我们把焦点放在16位色的模式上。我们把源点C2和Alpha通道点C1用Alpha混合,混合后得到点C,如果Alpha取0~1,公式如下:

C = C2*Alpha + C1*(1-Alpha)

如果Alpha取0~32,公式如下:

C = (C2*Alpha + C1*(1-Alpha))>>5

每个点由R,G,B 3个分量组成,所以上面的运算要分别对每个分量进行计算,如果整体计算,由于进位的关系我们会得到错误的结果。我们只考虑用得较多的565格式,即16位的颜色值为RRRRRGGGGGGBBBBB,555格式原理是一样的。显然我们每次处理一个点似乎只能按照“拆分-分别运算-拆分”来写代码,但是这样是低效的,想想1024*768模式下运算一帧要进行多少次运算,一定快不到哪里去。

  Intel有段很长的代码,我没仔细看,也没试验,总觉得不太可靠(呵呵)。还看了GameRes上的一些相关文章,还是有值得参考的地方,就是觉得看了还是有些茫然。

  下面说我的算法,首先说明这个快速算法是针对每个Alpha值建立一个函数进行运算,如果在一个函数里实现任意Alpha的运算,一次只能运算2个点,而且汇编代码是26行,而且有2次乘法,也用到了部分MMX加速。经过针对每一级Alpha的优化处理,每次处理4个点,代码只要8行左右,移位代替了乘法运算,完全发挥了MMX的威力。我只做了17级变换,0级和17级不用做,1到15原理一样,只有少少的不同,现在我举例半透明的算法,其他大家可以自己实现,有问题也可以和我交流。

  Alpha运算中每个点3个色素,每个色素都要按上面那个公式运算,也就是每个色素要做2次乘法和一次加发,尽管可以变换一下不做浮点运算,但性能又能提高多少?我先讲一下我算法的一个基本原理,即“任意分组移位”,意思就是把一个数中分为N组,每组位数并不要求相同,我们用一次移位和一次与运算就能做到好像是每个分组移位而互不影响的效果。比半透明下Alpha=0.5,换成移位就是>>1,我们先把C右移一位,然后AND 一个2进制的数0111101111101111(0x7BEF),就完成了3个色素同时*0.5的运算,简单吧。

  代码相信大家很容易就看懂了,大家把汇编部分和自己的程序结合就可以了,只要提供一些参数,比如页面数据指针和长度高度等资料.下次我会发布带Colorkey和Clip功能的代码,同样是MMX处理的,而且不用if(这会大大降低流水线的效率).以后还会介绍动态光源,灰度图,动画控制等高级主题,欢迎大家指导,由于水平和打字原因,可能文章中会有错误,请谅解.

下面是任意Alpha的混合运算

BOOL
CAresMaterial::DrawAlpha( LONG X, LONG Y, LPRECT pRect, BYTE Alpha )
{
    unsigned __int16 *pSrc, *pDest;
   
unsigned __int32 A, PA;
   
unsigned __int16 Width, Height;
   
unsigned __int32 D1, D2;
   
RECT Rect;

   
A = Alpha & 0x1F;
   
PA = 0x1F - A;
   
Width = (unsigned __int16)(pRect->right - pRect->left + 1);
   
Height = (unsigned __int16)(pRect->bottom - pRect->top + 1);
   
D1 = (m_Desc.dwPitch - Width + 1)<<1 ;
   
D2 = (m_Desc.pAres->GetScreenPitch() - Width + 1)<<1 ;
   
SetRect( &Rect, X, Y, X+Width-1, Y+Height-1 );
   
m_Desc.pAres->BackToDILayer( &Rect );
   
pSrc = m_Desc.pData + pRect->top*m_Desc.dwPitch + pRect->left;
   
pDest = m_Desc.pAres->GetDILayerData() + Y*m_Desc.pAres->GetScreenPitch() + X;


   
__asm
   
{
   
   
mov esi,pSrc
   
   
mov edi,pDest
   
   
movd mm2,A
   
   
movd mm3,PA

   
   
mov cx,Height
   
   
shl ecx,16
   
   
mov cx,Width

   
LOOPA:
   
   
ror ecx,16
   
   
dec cx
   
   
jz DONE
   
   
ror ecx,16

   
LOOPB:
   
   
dec cx
   
   
jz NEXTLINE
   
   
//Process one point
   
   
mov ax,[esi]
   
   
mov dx,ax
   
   
shl eax,16
   
   
mov ax,dx
   
   
and eax,0x7E0F81F
   
   
movd edx,mm2
   
   
mul edx
   
   
movd mm0,eax

   
   
mov ax,[edi]
   
   
mov dx,ax
   
   
shl eax,16
   
   
mov ax,dx
   
   
and eax,0x7E0F81F
   
   
movd edx,mm3
   
   
mul edx
   
   
movd mm1,eax

   
   
paddd mm0,mm1
   
   
psrlq mm0,5
   
   
movd eax,mm0
   
   
and eax,0x7E0F81F
   
   
mov edx,eax
   
   
shr edx,16
   
   
or eax,edx
   
   
mov [edi],ax

   
   
inc esi
   
   
inc edi
   
   
inc esi
   
   
inc edi
   
   
jmp LOOPB

   
NEXTLINE:
   
   
add esi,D1
   
   
add edi,D2
   
   
mov cx,Width
   
   
jmp LOOPA

   
DONE:
   
   
emms
   
}

   
m_Desc.pAres->DILayerToBack( &Rect );

   
return TRUE;
}



下面是半透明Alpha的混合运算

void
CAresMaterial::DrawAlpha1( LONG X, LONG Y, LPRECT pRect )
{

    unsigned __int16 *pSrc, *pDest;
    unsigned __int16 Width, Height, DW, DLeft;
    unsigned __int32 D1, D2;
    static unsigned __int64 MASKER = 0x7BEF7BEF7BEF7BEF;
    RECT Rect;

    Width = (unsigned __int16)(pRect->right - pRect->left);
    Height = (unsigned __int16)(pRect->bottom - pRect->top + 1 );
    pSrc = m_Desc.pData + pRect->top*m_Desc.dwPitch + pRect->left;
    pDest = m_Desc.pAres->GetBackData() + Y*m_Desc.pAres->GetScreenPitch() + X;

    DLeft = (Width % 4) + 1;
    DW = (Width>>2) + 1;

    D1 = (m_Desc.dwPitch - Width)<<1 ;
    D2 = (m_Desc.pAres->GetScreenPitch() - Width)<<1 ;
    SetRect( &Rect, X, Y, X+Width, Y+Height-1 );

    __asm
    {
        mov esi,pSrc
        mov edi,pDest
        mov bx,DLeft

        mov cx,Height
        shl ecx,16
        mov cx,DW

    LOOPA:
        ror ecx,16
        dec cx
        jz DONE
        ror ecx,16

    LOOPB:
        dec cx
        jz ENDLINE
        //Process four points once
        movq mm0,[esi]
        movq mm1,[edi]
        psrlq mm0,1
        psrlq mm1,1
        pand mm0,MASKER
        pand mm1,MASKER
        paddw mm0,mm1
        movq [edi],mm0

        add esi,8
        add edi,8
        jmp LOOPB

    ENDLINE:
        dec bx
        jz NEXTLINE
        mov ax,[esi]
        mov dx,[edi]
        shr ax,1
        shr dx,1
        and ax,0x7BEF
        and dx,0x7BEF
        add ax,dx
        mov [edi],ax
        inc esi
        inc esi
        inc edi
        inc edi
        jmp ENDLINE

    NEXTLINE:
        add esi,D1
        add edi,D2
        mov cx,DW
        mov bx,DLeft
        jmp LOOPA

    DONE:
        emms
    }
}

posted @ 2010-06-09 14:34 莫失莫忘 阅读(204) | 评论 (0)编辑 收藏

http://dev.csdn.net/htmls/42/42883.html
http://www.vckbase.com/document/viewdoc/?id=1440
http://g.csdn.net/5143319
http://d.download.csdn.net/down/889596/xuezhimeng
http://www.pconline.com.cn/download/heji/0901/1549110_1.html
http://cache.baidu.com/c?m=9d78d513d98401ae4fece4697b16c0126f43f1662ba0d4013894cd47c9221d03506790a63a644b5383966b6776ff1a07bcb7217240527de8869c9f4ba9e2da3f2efe223f701b854511d918&p=8c70c64ad4934eae13f1c9214e&user=baidu
http://tieba.baidu.com/f?kz=714213830
http://cache.baidu.com/c?m=9d78d513d9d437ae4f9ce3697c62c0161c4381132ba7a4020bd48438e2732d305321a3e52878564291d27d141cb20c19afe73605754476eb8cc8ff1b80e48f7e72cd7b6a2d4fd00012d01cb28a1b628171c70bb4f94efaffab6ec5f39792c208048c16583ad9eddd0d46479131f31631e2a59f49025f67e0ab6f3b&p=c6769a45c5d917ff57eb9435455f&user=baidu
http://www.baidu.com/s?tn=16site_5_pg&bs=%D3%CE%CF%B7%BC%DC%B9%B9&f=8&wd=%D3%CE%CF%B7%BF%F2%BC%DC
http://www.pudn.com/downloads217/sourcecode/windows/network/detail1023143.html
碰撞检测:
http://dev.gameres.com/Program/Visual/3D/3DCollision.mht
http://wenku.baidu.com/view/c317e65c3b3567ec102d8a58.html
http://tieba.baidu.com/f?kw=%D0%C7%BC%CA%B9%A4%D7%F7%CA%D2
2D碰撞检测引擎(相关文章):
http://www.baidu.com/s?bs=Irrlicht+%C5%F6%D7%B2%BC%EC%B2%E2&f=8&wd=Irrlicht+2D%C5%F6%D7%B2%BC%EC%B2%E2
http://blog.csdn.net/aladins/archive/2007/01/14/1482595.aspx
http://www.baidu.com/s?wd=SDL%C5%F6%D7%B2%BC%EC%B2%E2
http://www.verycd.com/files/955e419766a2ed8672472f3ce4180c8c22445512
SDL:
http://cache.baidu.com/c?m=9f65cb4a8c8507ed4fece763105392230e54f729778693027fa3cf1fd5790f1c0721bca66178505380936b6777ac4257e0fb3565377523a09bbe8b5dddccc86e70d633712d5cd04e538547b8ca3632b02a872d99b868e6ad813584afa2c4af5544be54120a80e7fb2c&p=8b2a9503cc934eae52bacd3e510c&user=baidu
http://wenku.baidu.com/view/5ecb3b0cba1aa8114431d91b.html
关于游戏编程的一些其他的东西
http://www.fish888.com/Quake2-MD2-t172272
http://zhidao.baidu.com/question/5641067.html
http://bbs.gameres.com/showthread.asp?threadid=147168
http://www.baidu.com/s?wd=C%D3%CE%CF%B7%B1%E0%B3%CC%B4%D3%C8%EB%C3%C5%B5%BD%BE%AB%CD%A8
http://dev.gameres.com/Program/Abstract/Thinking/RLE.htm
http://www.baidu.com/s?bs=2D%D3%CE%CF%B7+%C5%F6%D7%B2%BC%EC%B2%E2%BF%E2&f=8&wd=%D3%CE%CF%B7+%C5%F6%D7%B2%BC%EC%B2%E2%BF%E2
模拟城市的源代码
http://resource.gameres.com/simcity_source.zip

如果这个没有刷出来就用关键字:用SDL打造我们自己的战棋游戏。

  通过运行时类型识别(RTTI)(Run-Time Type Information),程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。   RTTI提供了一下两个非常有用的操作符:   (1)typeid操作符,返回指针和引用所指的实际类型;   (2)dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。   面向对象的编程语言,象C++,Java,delphi都提供了对RTTI的支持。 本文将简略介绍 RTTI 的一些背景知识、描述 RTTI 的概念,并通过具体例子和代码介绍什么时候使用以及如何使用 RTTI;本文还将详细描述两个重要的 RTTI 运算符的使用方法,它们是 typeid 和 dynamic_cast。   其实,RTTI 在C++中并不是什么新的东西,它早在十多年以前就已经出现了。但是大多数开发人员,包括许多高层次的C++程序员对它并不怎么熟悉,更不用说使用 RTTI 来设计和编写应用程序了。   一些面向对象专家在传播自己的设计理念时,大多都主张在设计和开发中明智地使用虚拟成员函数,而不用 RTTI 机制。但是,在很多情况下,虚拟函数无法克服本身的局限。每每涉及到处理异类容器和根基类层次(如 MFC)时,不可避免要对对象类型进行动态判断,也就是动态类型的侦测。如何确定对象的动态类型呢?答案是使用内建的 RTTI 中的运算符:typeid 和 dynamic_cast。   在C++中存在虚函数,也就存在了多态性,对于多态性的对象,在程序编译时可能会出现无法确定对象的类型的情况。当类中含有虚函数时,其基类的指针就可以指向任何派生类的对象,这时就有可能不知道基类指针到底指向的是哪个对象的情况,类型的确定要在运行时利用运行时类型标识做出。为了获得一个对象的类型可以使用typeid函数,该函数反回一个对type_info类对象的引用,要使用typeid必须使用头文件<typeinfo>,因为typeid是一个反回类型为typ_info的引用的函数所以这里有必要先介绍一下type_info类    1、typid函数:该函数的主要作用就是让用户知道当前的变量是什么类型的,比如使用typid(a).name()就能知道变量a是什么类型的。因为typid()函数是一个反回类型为typid_info类型的函数,所以下面先对type_info类作下介绍   2、type_info类:该类的具体实现方式依编译器而定,但一般都有如下的成员定义   class type_info   {private:   type_info(const type_info &);   type_info& operator =(const type_info&); //type_info类的复制构造函数和赋值运算符是私有的。   public:   virtual ~type_info(); //析构函数   bool operator = =(const type_info&) const; //在type_info类中重载了= =运算符,该运算符可以比较两个对象的类型是否相等。   bool operator !=(const type_info&)const; //重载的!=运算符,以比较两个对象的类型是否不相等   const char * name() const; //使用得较多的成员函数name,该函数反回对象的类型的名字。前面使用的typeid(a).name()就调用了该成员函数   bool before(const type_info&);};   因为type_info类的复制构造函数和赋值运算符都是私有的,所以不允许用户自已创建type_info的类,比如type_info A;错误,没有默认的构造函数。唯一要使用type_info类的方法就是使用typeid函数。   3、typeid函数怎样创建type_info类的对象:该函数反回type_info类对象的引用,即形式为type_info& typid();因此也可以说typid函数是type_info类的一个引用对象,可以访问type_info类的成员。但因为不能创建type_info类的对象,而typeid又必须反回一个类型为type_info类型的对象的引用,所以怎样在typeid函数中创建一个type_info类的对象以便让函数反回type_info类对象的引用就成了问题。这可能是把typid函数声明为了type_info类的友元函数来实现的,默认构造函数是私有的并不能阻止该类的友元函数创建该类的对象。所以typeid函数如果是友元的话就可以访问type_info类的私有成员,从而可以创建type_info类的对象,从而可以创建反回类型为type_info类的引用。举个例子class A{private:A(){} A(const A&){} A& operator =(const A&){} friend A& f();};这里把类A的默认构造函数,复制构造函数和赋值操作符定为私有从而防止创建类A的对象,但函数f()是类A的友元,所以在函数f()中可以创建类A的对象。同时为了实现函数f()反回的对象类型是A的引用,就必须在函数f中创建一个类A的对象以作为函数f的反回值,比如函数f可以这样定义A& f(){A ma; cout<<”f”<<endl; return ma}。   4、因为typeid函数是type_info类的对象,也就是说可以用该函数访问type_info类的成员,即type_info类中重载的= =和!=运算符,name()和before()成员函数,比如typid(a).naem()和typid(a)= =typid(b)等等。   5、typeid函数的使用原理:该函数的形式为type_info& typeid(object)其中object是任何类型的对象,可以是内置类型和用户创建的类类型。可以看出typeid即是一个函数,同时他也是type_info类的对象,即typeid可以访问类type_info类的成员,也可以做为一个单独的函数来使用。做个简单的例子,比如   class A{private: A(){b=3;cout<<”A”<<endl;} //私有的默认构造函数   public: void name(){cout<<”NA”<<endl;} int b;   friend A f();}; //函数f()是类A的友元,因此在f中可以创建类A的对象。   A f() //函数f()在这里即是类A的一个对象,也是一个单独的函数。   { A m; //创建类A的对象,因为函数f是类A的友元,因此可以创建类A的对象   cout<<”F”<<endl; return m;}   main()   { f().name(); //函数f()作为类A的对象使用,这里要注意程序的执行顺序,首先执行函数f()中的语句A m,因此调用类A的默认构造函数输出A,然后执行A m;后面的语句,输出F,再然后调用类A中的成员函数name输出NA.   f(); } //函数f()单独作为函数使用。   我们创建一个类A,其中A的默认构造函数是私有的,也就是说不能用默认构造函数创建类A的对象。函数f()是类A的友元,且反回一个类A的对象,因为f()函数是类A的友元,所以在函数f中可以用默认构造函数创建类A的对象,这时函数f()同时是一个函数,也是类A的对象,因此也可以访问类A中的成员。   6、typeid函数使用方式一:使用type_info类中的name()成员函数反回对象的类型的名称。其方法为:typeid(object).name()其中object是要显示的对象的类型名,该函数反回的名字因编译器而定。这里要注意的就是使用方式一中提到的虚函数类型的问题,即如果有类A,且有虚函数,类B,C,D都是从类A派生的,且都重定义了类A中的虚函数,这时有类A的指针p,再把对象类B的对象的地址赋给指针p,则typeid(p).name()将反回的类型将是A*,因为这里的p表示的是一个指针,该指针是类型为A的指针,所以反回A*,而typeid(*p).name()将反回B,因为指针p是指向类B的对象的,而*p就表示的是类B的对象的类型,所以反回B。   7、typeid函数使用方式二:使用type_info类中重载的= =与!=比较两个对象的类型是否相等。使用该方法需要调用类type_info中重载的= =和!=操作符,其使用方法为typid(object1)= =typid(object2);如果两个对象的类型相等则反回1,如果不相等则为0。这种使用方法通常用于比较两个带有虚函数的类的对象是否相等,比如有类A,其中定义有虚函数,而类B,类C,类D,都是从类A派生而来的且重定义了该虚函数,这时有个类A的指针p和p1,按照虚函数的原理,基类的指针可以指向任何派生类的对象,在这时就有可能需要比较两个指针是否指向同一个对象,这时就可以这样使用typeid了,typeid(*p)= =typeid(*p1);这里要注意的是typeid(*p)与typeid(p)是指的不同的对象类型,typeid(p)表示的是p的类型,在这里p是一个指针,这个指针指向的是类A的对象,所以p的类型是A*,而typeid(*p)则不一样,*p表示的是指针p实际所指的对象的类型,比如这里的指针p指向派生类B,则typeid(*p)的类型为B。所以在测试两个指针的类型是否是相等时应使用*p,即typeid(*p)= =typeid(*p1)。如果是typeid(p)= =typeid(p1)的话,则无论指针p和p1指向的什么派生类对象,他们都是相等的,因为都是A *的类型。   8、强制类型转换运算符:C++有四种强制类型转换符,分别是dynamic_cast,const_cast,static_cast,reinterpret_cast。其中dynamic_cast与运行时类型转换密切相关,在这里我们先介绍dynamic_cast,其他三种在后面介绍。   8.1、dynamic_cast强制转换运算符:该转换符用于将一个指向派生类的基类指针或引用转换为派生类的指针或引用,注意dynamic_cast转换符只能用于含有虚函数的类,其表达式为dynamic_cast<类型>(表达式),其中的类型是指把表达式要转换成的目标类型,比如含有虚函数的基类B和从基类B派生出的派生类D,则B *pb; D *pd, md; pb=&md; pd=dynamic<D*>(pb); 最后一条语句表示把指向派生类D的基类指针pb转换为派生类D的指针,然后将这个指针赋给派生类D的指针pd,有人可能会觉得这样做没有意义,既然指针pd要指向派生类为什么不pd=&md;这样做更直接呢?有些时候我们需要强制转换,比如如果指向派生类的基类指针B想访问派生类D中的除虚函数之外的成员时就需要把该指针转换为指向派生类D的指针,以达到访问派生类D中特有的成员的目的,比如派生类D中含有特有的成员函数g(),这时可以这样来访问该成员dynamic_cast<D*>(pb)->g();因为dynamic_cast转换后的结果是一个指向派生类的指针,所以可以这样访问派生类中特有的成员。但是该语句不影响原来的指针的类型,即基类指针pb仍然是指向基类B的。如果单独使用该指针仍然不能访问派生类中特有的成员。一般情况下不推见这样使用dynamic_cast转换符,因为dynamic_cast的转换并不会总是成功的,具体情况在后面介绍。   8.2、dynamic_cast的注意事项:dynamic_cast转换符只能用于指针或者引用。dynamic_cast转换符只能用于含有虚函数的类。dynamic_cast转换操作符在执行类型转换时首先将检查能否成功转换,如果能成功转换则转换之,如果转换失败,如果是指针则反回一个0值,如果是转换的是引用,则抛出一个bad_cast异常,所以在使用dynamic_cast转换之间应使用if语句对其转换成功与否进行测试,比如pd=dynamic_cast<D*>(pb); if(pd){…}else{…},或者这样测试if(dynamic_cast<D*>(pb)){…}else{…}。   8.3、const_cast操作符:其表达式为const_cast<类型>(表达式),其中类型指要把表达式转换为的目标类型。该操作符用于改变const和volatile,const_cast最常用的用途就是删除const属性,如果某个变量在大多数时候是常量,而在某个时候又是需要修改的,这时就可以使用const_cast操作符了。const_cast操作符不能改变类型的其他方面,他只能改变const或volatile,即const_cast不能把int改变为double,但可以把const int改变为int。const_cast只能用于指针或引用。const_cast的用法举例比如:int a=3; const int *b=&a; int* c; c=const_cast<int*>(b); *c=4; cout<<a<<*c;这时输出两个4,如果不使用const_cast转换符则常量指针*c的值是不能改变的,在这里使用const_cast操作符,通过指针b就能改变常量指针和变量a的值。   8.4、static_cast操作符:该操作符用于非多态类型的转换,任何标准转换都可以使用他,即static_cast可以把int转换为double,但不能把两个不相关的类对象进行转换,比如类A不能转换为一个不相关的类B类型。static_cast本质上是传统c语言强制转换的替代品。   8.5、reinterpret_cast操作符:该操作符用于将一种类型转换为另一种不同的类型,比如可以把一个整型转换为一个指针,或把一个指针转换为一个整型,因此使用该操作符的危险性较高,一般不应使用该操作符。   9、使用 typeid 要注意一个问题,那就是某些编译器(如 Visual C++)默认状态是禁用 RTTI 的,目的是消除性能上的开销。如果你的程序确实使用了 RTTI,一定要记住在编译前启用 RTTI。使用 typeid 可能产生一些将来的维护问题。假设你决定扩展上述的类层次,从MediaFile 派生另一个叫 LocalizeMedia 的类,用这个类表示带有不同语言说明文字的媒体文件。但 LocalizeMedia 本质上还是个 MediaFile 类型的文件。因此,当用户在该类文件图标上单击右键时,文件管理器必须提供一个“播放”菜单。可惜 build()成员函数会调用失败,原因是你没有检查这种特定的文件类型。为了解决这个问题,你必须象下面这样对 build() 打补丁:   void menu::build(const File * pfile)   {   //......   else if (typeid(*pfile)==typeid(LocalizedMedia))   {   add_option("play");   }   }   唉,这种做法真是显得太业余了,以后每次添加新的类,毫无疑问都必须打类似的补丁。显然,这不是一个理想的解决方案。这个时候我们就要用到 dynamic_cast,这个运算符用于多态编程中保证在运行时发生正确的转换(即编译器无法验证是否发生正确的转换)。用它来确定某个对象是 MediaFile 对象还是它的派生类对象。dynamic_cast 常用于从多态编程基类指针向派生类指针的向下类型转换。它有两个参数:一个是类型名;另一个是多态对象的指针或引用。其功能是在运行时将对象强制转换为目标类型并返回布尔型结果R簿褪撬担绻煤晒Φ夭⑶沂嵌慕?*pfile 强制转换为 MediaFile,那么 pfile的动态类型是 MediaFile 或者是它的派生类。否则,pfile 则为其它的类型:   void menu::build(const File * pfile)   {   if (dynamic_cast <MediaFile *> (pfile))   {   // pfile 是 MediaFile 或者是MediaFile的派生类 LocalizedMedia   add_option("play");   }   else if (dynamic_cast <TextFile*> (pfile))   {   // pfile 是 TextFile 是TextFile的派生类   add_option("edit");   }   }   细细想一下,虽然使用 dynamic_cast 确实很好地解决了我们的问题,但也需要我们付出代价,那就是与 typeid 相比,dynamic_cast 不是一个常量时间的操作。为了确定是否能完成强制类型转换,dynamic_cast`必须在运行时进行一些转换细节操作。因此在使用 dynamic_cast 操作时,应该权衡对性能的影响。

posted @ 2010-06-07 17:02 莫失莫忘 阅读(141) | 评论 (0)编辑 收藏

         我们现在普遍用的是DX9了,但是DX8就已经没有DirectDraw了,已经被整合进入DirectX Graphics了。
         我现在在看《windows游戏编程大师技巧》,里面是从DirectDraw开始讲解的,现在基本都是D3D了,所以我
要不要直接看D3D呢?DirectDraw只是作为基本了解。
        了解到DX9下除了用DirectDraw7外,就只能用D3D接口模拟呢!还有个D3DXSprite的头文件可以用。
        我的D3D基本入门而已,书上说DirectDraw是D3D的基础,我怕错过了一些关键的只是,所以到过头来看DirectDraw
了。
        大家对我的学习过程有没有更好的建议?!指教下
         那本《openGL超级宝典(第四版)》是不是等我D3D比较可以了的时候的必看书籍,我需要对计算机的图像原理
更加了解。
        有没有人知道DirectDraw现在还有哪些方面的应用?
        (顺便抱怨下,CPPBLOG的这个写日志的系统好慢)

posted @ 2009-10-20 18:13 莫失莫忘 阅读(128) | 评论 (0)编辑 收藏

仅列出标题
共2页: 1 2 

posts - 15, comments - 0, trackbacks - 0, articles - 0

Copyright © 莫失莫忘