博文
一个精悍而巧妙的程序(2006-8-4 16:29:00)
近期用CreateProcess的时候出了些问题,发现第一个时间片的末尾总会有重复的指令在执行,于是想看看_beginthreadex能不能解决这个问题,无奈_beginthreadex却不接受我的线程函数,问了问v兄,v兄也说不清楚原因,给出了互斥体的方法来解决。问题虽然解决了,但是无法用_beginthreadex总让我觉得很不爽,毕竟谁知道CreateProcess什么时候会出点问题呢。于是到处搜索资料,却意外的发现了一个叹为观止的进程间的自我通信,虽然与我本来的目的相差了很多,但是这个程序巧妙的构思让我兴奋不已,不敢独享,拿出来让大家也欣赏一下:
#include"iostream.h"
#include"windows.h"
static LPCTSTR g_szContinueEvent="w2kdg.EventDemo.event.Continue";
BOOL CreateChild(LPCTSTR szAppName)
{
STARTUPINFO si;
::ZeroMemory(reinterpret_cast<void*>(&si),sizeof(si));
si.cb=sizeof(si);
PROCESS_INFORMATION pi;
BOOL bCreateOK=::CreateProcess(szAppName,
"child",
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi);
if(bCreateOK)
{
::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);
}
return bCreateOK;
}
void WaitForChild(LPCTSTR szAppName)
{
HANDLE hEventContinue=::CreateEvent(NULL,
TRUE,
FALSE,
g_szContinueEvent);
if(hEventContinue!=NULL)
{
if(::CreateChild(szAppName))
{
::WaitForSingleObject(hEventContinue,INFINITE);
}
::CloseHandle(hEventContinue);
hEventContinue=INVALID_HANDLE_VALUE;
}
}
void SignalParent()
{
HANDLE hEventContinue=::OpenEvent(EVENT_MODIFY_STATE,
FALSE,
g_szContinueEvent);
if(hEventContinue!=NULL)
{
::SetEvent(hEventContinue);
}
::CloseHandle(hEventContinue);
hEventContinue=INVALID_HANDLE_VALUE;
}
int main(int argc,char *argv[])
{
cout<<"\n"<<argv[0]<<endl;
if(::strcmp(argv[0],"child")!=0)
{
cout<<"Parent waiting on child"<<endl;
::WaitForChild(argv[0]);
cout<<"Parent released"<<endl;
}
else
{
::SignalParent();
}
return 0;
}
阅读全文(2189) | 评论:5 | 复制链接
OpenGL基本框架(2006-8-4 16:09:00)
{
static PIXELFORMATDESCRIPTOR pfd=
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
24,
0,0,0,0,0,0,
0,
0,
0,
0,0,0,0,
32,
0,
0,
PFD_MAIN_PLANE,
0,
0,0,0
};
int pixelformat; //下面两个if用来选择与pDC最匹配的硬件像素格式
if((pixelformat=ChoosePixelFormat(pDC->GetSafeHdc(),&pfd))==0)
{
MessageBox("ChoosePixelFormat failed");
return FALSE;
}
if(SetPixelFormat(pDC->GetSafeHdc(),pixelformat,&pfd)==FALSE)
{
MessageBox("SetPixelFormat failed");
return FALSE;
}
return TRUE;
}
----------------------------------------------------------------------------
void CGdlg::CreateRC() //创建RC的一系列操作
{
PIXELFORMATDESCRIPTOR pdf;
int n;
HGLRC hrc; //rc句柄
pDC=new CClientDC(this); //pDC声明于类定义中: CClientDC *pDC
ASSERT(pDC!=NULL);
if(!InitialPixelFormat()) //调用前面定义的函数设置像素格式
{
return;
}
n=::GetPixelFormat(pDC->GetSafeHdc()); //这两行用于测试像素格式
::DescribePixelFormat(pDC->GetSafeHdc(),n,sizeof(pfd),&pfd);
hrc=wglCreateContext(pDC->GetSafeHdc());//创建rc
wglMakeCurrent(pDC->GetSafeHdc(),hrc); //使rc当前化
}
---------------------------------------------------------------------------
void CGdlg::FirstDraw() //初始化
{
glClearDepth(1.0f); //设置深度缓存
glEnable(GL_DEPTH_TEST); //启用深度测试
glDepthFunc(GL_LEQUAL); //深度测试的类型
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
---------------------------------------------------------------------------
void CGdlg::ReleaseRC() //释放RC
{
HGLRC hrc;
hrc=::wglGetCurrentContext();
::wglMakeCurrent(NULL,NULL);
if(hrc)
{
::wglDeleteContext(hrc);
}
if(pDC)
{
delete pDC;
}
}
----------------------------------------------------------------------------
void CGdlg::ReDraw() //用于OnDraw/OnPaint
{
static BOOL bBusy=FALSE;
if(bBusy)
{
return;
}
bBusy=TRUE;
glClearColor(0.2f,0.2f,0.5f,1.0f); //设置背景色
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//刷新深度缓存
//以及背景色
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//DrawOG(); //具体绘图(如果用glClearColor设置了背景色,
//那么要记得用glClear刷新
glFinish();
SwapBuffers(wglGetCurrentDC());
bBusy=FALSE;
}
---------------------------------------------------------------------------
开始OpenGl操作所对应的消息函数内容应该包含:
CreateRC();
FirstDraw();
Invalidate(); (或者RedrawWindow(),关于两者的区别,请看注)
----------------------------------------------------------------------------
结束OpenGl操作所对应的消息函数内容应该包含:
ReleaseRC();
----------------------------------------------------------------------------
OnDraw/OnPaint的内容应该包含:
ReDraw();
----------------------------------------------------------------------------
注一:
1 使用Invalidate(..)可以触发WM_PAINT,但系统并不立即重绘,这是因为WM_PAINT的2个特性:low priority、Windows combines multiple WM_PAINT messages in the message queue into a single message, 如果想立即重绘,那么就需要调用UpdateWindow();
2 RedrawWindow()可以实现立即重绘;
3 如果更新的是一个区域RGN的时候它没法调用Invalidate实现,因为Invalidate只对矩形有效,但是RedrawWindow可以。
如果要使用GDI函数,必须在glFinish()和SwapBuffers(wglGetCurrentDC())函数之后调用,否则会产生闪烁。
如果一定要在绘制OpenGL结束之前使用GDI函数,那么想要除去闪烁则只能使用SingleBuffer模式。
阅读全文(2062) | 评论:6 | 复制链接
vc之pragma宏的简单应用(2006-8-4 16:08:00)
-----------------------------------------------------------------------
2 When the compiler encounters a deprecated symbol, it issues C4995:
void func2(void) {}
int main() {
func1();
func2();
#pragma deprecated(func1, func2)
func1(); // C4995
func2(); // C4995
}
----------------------------------------------------------------------
3 The following code fragment uses the message pragma to display a message during compilation:
#pragma message( "Pentium processor build" )
#endif
-----------------------------------------------------------------------
4 The warning-number-list can contain any warning numbers. Multiple options can be specified in the same pragma directive as follows:
// 4507 and 4034.
#pragma warning( once : 4385 ) // Issue warning 4385
// only once.
#pragma warning( error : 164 ) // Report warning 4164
// as an error.
-----------------------------------------------------------------------
5 This pragma specifies packing alignment for structure, union, and class members,the parameter can only be 1,2,4,8,16,default is 8:
注:如果设置的值比结构体中字节最长的类型还要大,则这个变量(注意仅针对这一个变量)只按照它的字节长度对齐,即不会出现内存浪费的情况。请参见(4)。
#pragma pack(1) //每个变量按照1字节对齐
struct A
{
char x; //aligned on byte boundary 0
int y; //aligned on byte boundary 1
}a;
sizeof(a)==5
#pragma pack(2) //每个变量按照2字节对齐
struct A
{
char x; //aligned on byte boundary 0
int y; //aligned on byte boundary 2
}a;
sizeof(a)==6
#pragma pack(4) //每个变量按照4字节对齐
struct A
{
char x; //aligned on byte boundary 0
int y; //aligned on byte boundary 4
}a;
sizeof(a)==8
#pragma pack() //默认,相当于#pragma pack(8) 每个变量按照8字节对齐
struct A
{
char x; //aligned on byte boundary 0
int y; //aligned on byte boundary 4
}a;
sizeof(a)==8
但是这里y的大小是4字节,所以不会按照8字节对齐,否则将造成1个int空间的浪费
阅读全文(2454) | 评论:1 | 复制链接
宽字符集下汉字的输出(2006-8-4 16:08:00)
char str[100];
像下面这样声明函数原形:
void strcpy( char *out, char *in );
为了将上面的声明改成支持双字节的 UNICODE 字符集,可以用下面的方法:
wchar_t str[100];以及void wcscpy( wchar_t *out, wchar_t *in );
Project=>Settings=>C/C++标签=>Preprocessor=>预处理定义中加上:
UNICODE和_UNICODE。其中_UNICODE宏用于C运行时头文件,而UNICODE宏则用于Windows头文件。当编译源代码模块时,通常要同时定义这两个宏,因为不同的头文件可能使用不同的宏。
如何表示Unicode字符串常量?
字符集 实例
ANSI “string”
Unicode L“string”
ANSI/Unicode TEXT(“string”) 或 __TEXT(“string”) (有两条下划线)
如何表示Unicode字符数组?
ANSI char a[N]
Unicode wchar_t a[N]
ANSI/Unicode TCHAR a[N]
上述的ANSI/Unicode是通用型,由是否定义了UNICODE来决定是编译为ANSI,还是编译为UNICODE。
#include<locale.h>
int main(int argc, char *argv[])
{
wchar_t cur_buff[]=L"你";
_wsetlocale(LC_ALL,L"chs"); //vc6必须加,devcpp不能加。
std::wcout<<cur_buff; //c语言可以用stdio.h中的wprintf(cur_buff);
return 0;
}
阅读全文(2088) | 评论:2 | 复制链接
vc6中关于模板显式特化问题的解决(2006-8-4 16:07:00)
#include <string.h>
{
return (t1>t2?t1:t2);
}
typedef const char* pcc;
template<> pcc max(pcc s1,pcc s2)
{
return (strcmp(s1,s2)>0?s1:s2);
}
void main()
{
int n=max(4,3);
cout<<n<<endl;
const char *p=max("bbb","aaa");
cout<<p;
}
#include <string.h>
{
return (t1>t2?t1:t2);
}
typedef const char* pcc;
template<> pcc max(pcc s1,pcc s2)
{
return (strcmp(s1,s2)>0?s1:s2);
}
void main()
{
int n=max(4,3);
cout<<n<<endl;
const char *p=max<pcc>("bbb","aaa"); //这里,要“特别强调”pcc这个类型。
cout<<p;
}
阅读全文(1437) | 评论:0 | 复制链接
左值与右值(2006-8-4 16:06:00)
比如:
int ii = 5; //ii是左值,5是右值
int jj = ii; //jj是左值,ii是右值
上面表明,左值肯定可以作为右值使用,但反之则不然。左值和右值的最早区别就在于能否改变。左值是可以变的,右值不能变,但是这一点在C++中已经不再成立。
左值表示程序中必须有一个特定的名字引用到这个值。
右值表示程序中没有一个特定的名字引用到这个值。
这跟它们是否可以改变,是否在栈或堆(stack or heap)中毫无关系。
在下面的代码中:
int ii = 5;
const int jj = ii;
int a[5];
a[0] = 100;
*(a+3) = 200;
const int& max(const int& a, const int& b) //call by reference
{
return a > b ? a : b;
}
int& fun(int& a) //call by reference
{
a += 5;
return a;
}
ii,jj,a[0],*(a+3)这些值,还有函数max的返回值比如max(ii, jj),函数fun的返回值fun(ii)都是左值。因为它们都是被特定的名字所引用的值。
ii,jj,a[0],*(a+3),以及引用了max和fun的返回值的变量就是它们的名字。
(reference to const)可以引用到右值,所以max(8, 9)似乎应该是右值。但根据我们对左值的定义,说它是左值也对。不管它是左值,还是右值,我们都不能试图去改变它。为了与前面的概念一致,姑且认为它是左值,不可改变的常量左值。
比如下面的代码都是成立的:
ii = 600;
a[0] = 700;
fun(ii) = 800; //OK!
2.右值
没有特定名字的值是右值。
看下面的代码:
int fun1() //call by value
{
…
}
int* fun2() //call by value
{
…
}
函数fun1的返回值fun1(),函数fun2的返回值fun2()都是右值,它们的值都没有特定的名字去引用。
也许有人会奇怪,fun2()也是右值?前面说的max(a,b)不是左值吗?请看清楚,函数fun2的返回值是pointer,pointer也是call by value,而函数max的返
回值是reference,reference是call by reference。
当然字面上的(literal)值,比如5,8.23,’a’等等也都是右值。
C++中有可以改变的右值。那就是用户自定义的类(class)的构造函数生成的临时对象。
至于原因,我思考了一下,我想是这样的:我们看类(class)的数据布置结构,会发现它的每一个数据成员都是有名字的,我想编译器在编译的过程中,都会生成一个外部不所知的对这个临时对象右值的名字引用。但当需要改变这个临时对象的时候,这个名字就用上了(实际这时变成了左值,因为有了名字引用)。
比如:
class Point
{
public:
int x;
……//其他各种成员函数
};
Point().x = 6;//改变了右值
阅读全文(2257) | 评论:2 | 复制链接
重载、隐藏与覆盖(2006-8-4 16:05:00)
假设有:
class A
{
public:
void func(){}
};
class B: public class A
{
public:
void func(){}
};
那么,对于A* p;不管p指向A还是B,都只能调用A中的func;
同样,对于B* p;不管p指向A还是B,都只能调用B中的func;
总结:对于普通成员函数,调用的是哪个类的版本,要看指针的原型,而不是看指向对象的类型。
假设有:
class A
{
public:
virtual void func(){}
};
class B: public class A
{
public:
void func(){}
};
那么,对于A a;p=&a;不管p是A*还是B*,都调用A中的func;
同样,对于B b;p=&b;不管p是A*还是B*,都调用B中的func;
总结:对于虚函数,调用的是哪个类的版本,要看指针指向什么类型的对象,而不是看指针的原型。
即实现同名函数。重载与类无关,更与virtual无关。一般的函数就可以实现。仅仅是返回类型不同是不能重载的,重载至少需要在参数个数、参数类型、参数顺序这三者之中的一个不同。
阅读全文(1423) | 评论:0 | 复制链接
字符指针的初始化(2006-8-4 16:04:00)
在 C 中:char *p="abc"是完全合乎规则的事情
在 C++ 中:由于有特殊规定,所以这样也可以。
char *p="abc" 能不能编译通过要看你使用的编译器。鉴于大量遗留代码的存在,大部分编译器允许其通过,或者给个警告。当然,程序员自己必须保证绝不去修改其值:
a. 程序员不应该在代码中出现*p='A'这样的语句。这是当初约定好了的:编译器允许char *p="abc"通过,而程序员保证不去修改它。
b. *p='A'编译时应该允许通过,因为单就这条语句而言,它完全合法。
c. 运行时*p='A'能不能通过要看实际的运行环境,包括你使用的操作系统、编译器、编译器选项 等等,一句话,其运行结果由不得你,且不应该由你去关心,因为这种行为本身已经违反约定了。
阅读全文(1772) | 评论:3 | 复制链接


最新评论