taocoding


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

最简单的MFC程序

发表于 2007-01-21 | 分类于 VC
<TABLE style="TABLE-LAYOUT: fixed">

#include <afxwin.h>

//定义一个CWinApp的派生类
class CMinApp:public CWinApp
{
public:
virtual BOOL InitInstance();
};
//重载CWinApp成员函数InitInstance()
BOOL CMinApp::InitInstance()                     //应用程序初始化
{
CFrameWnd* pFrame=new CFrameWnd;     //动态生成主窗口类对象
pFrame->Create(0,_T(“A Minimal MFC Program”)); //创建主窗口
pFrame->ShowWindow(SW_SHOWMAXIMIZED); //显示主窗口
pFrame->UpdateWindow();                                         //刷新主窗口
AfxGetApp()->m_pMainWnd=pFrame;                   //指定应用程序主窗口
return TRUE;
}

//定义一个全局CMinApp对象
CMinApp HelloApp;                                      //生成应用程序对象并执行应用程序

在程序的一开始由CWinApp派生的一个自己的类CMinApp。这个类利用CWinApp中的虚拟初始化函数,改写成自己的初始化函数。在这个初始化函数中动态地建立了CFrameWnd类实例,用来定义窗口和显示窗口。而CWinApp自身去完成建立消息循环的工作,在这个小例子中没有处理消息。上述工作实际上还停留在定义上,程序的运行只有最后一句话,派生类CMinApp的实例化。

在MFC变成结构中CWinApp相当于WinMain()函数,其中的InitInstance函数是MFC应用程序的入口点,且它是可以重载的,可以应用它编写自己应用程序的初始化。InitInstance函数完成以下工作:

1:定义框架窗口对CFrameWnd作为应用程序的主窗口

2:显示主窗口

3:建立局部窗口与应用程序对象的关系

声明应用程序对象时,应用程序自动运行,因为类的构造器调用了run()函数,在run()中建立消息循环

CFrameWnd类负责窗口的创建和显示

从上面的例子中可以看出MFC程序至少要有两个对象,应用对象和主框架对象,在MFC应用程序中需要定义一个单独的全局应用程序对象。类CFrameWnd的对象代表中应用程序的主框架窗口。主框架窗口的创建和显示工作由CMinApp的成员函数InitInstance完成,在应用程序中必须重载该函数,因为CWinApp基类根本无法知道具体应用程序需要什么样的主框架窗口。应用程序运行开始时,Windows会自动调用应用程序框架内部的WinMain函数(在MFC应用程序中仍然存在WinMain函数作为程序的入口,只不过它被隐藏在应用程序内部了)WinMain函数会去查找应用程序的全局构造对象,然后调用InitInstance函数以进行必要的初始化,接着调用隐藏在CWinApp基类中的函数Run(),应用程序进入运行状态。用户可以通过关闭主窗口来终止应用程序,这一操作会引起一系列事件的发生,首先CFrameWnd对象将被删除,然后退出WinMain,最后删除CMinApp对象。


派生类的拷贝构造函数

发表于 2006-12-25 | 分类于 C++
<DIV class=cnt id=blog_text>

刚刚弄了一个关于派生类的实验,里面涉及到了派生类的拷贝构造函数,这时才发现自己在这个知识点上还很模糊。在CSDN上面看了下相关的一篇文章后,稍微有了点感觉。现总以一个例子总结如下:

情况(1). 派生类的copy   constructor未定义

#include   <iostream>  
using   namespace   std   ;  
   
class   base  
{  
public:  
base()   {   cout   <<   “base::base()”   <<   endl   ;   }  
base(   const   base&   )   {   cout   <<   “base::base(   const   base&)”   <<   endl   ;   }  
virtual   ~base()   {   cout   <<   “base::~base()”   <<   endl   ;   }  
};  
   
class   child   :   public   base  
{  
   
public:  
child()   {   cout   <<   “child::child()”     <<   endl   ;   }  
/——————————————————————  
child(   const   child&   )   {   cout   <<   “child::child(   const   child&   )” <<   endl   ;   }     
——————————————————————-
/  
~child()   {   cout   <<   “child::~child()”   <<   endl   ;   }  
int   test(int   i)   {   cout   <<   “int   child::test(int)”   <<   endl   ;  

return   0   ;   }  
   
};  
   
int main()  
{  
child   c1   ;  
cout   <<   “—————————-“   <<   endl   ;  
child   c2(c1)   ;  
cout   <<   “—————————-“   <<   endl   ;  
}  
输出结果:  
base::base()  
child::child()  
—————————-  
base::base(   const   base&)
—————————-  
child::~child()  
base::~base()  
child::~child()  
base::~base()  

注意 child   c2(c1)   ;   调用了基类的拷贝构造函数,这是为什么呢?因为派生类没有显式定义自己的拷贝构造函数,而基类定义了。首先child公有继承base,且base::copy   ctor也是public的,那么对child类而言,基类copy   ctor是可见的。 child   c2(c1)   ;   这句,编译器先找child自己的copy   ctor,没有。这时就去找基类的copy   ctor。由于它的参数是这样定义的:const   base&   ,即用到了基类对象的引用,所以这里有多态的能力,比如child   c2(c1)   ;

看下面这个例子

#include   <iostream>  
using   namespace   std   ;  
   
class   base  
{  
public:  
base(){cout<<”base::base()”<< endl;}  
base(const base& rhs)  
{  
cout<<”base::base(const base&)”<<endl;    
rhs.show();    
}  
virtual   void show() const {cout<<”base::show()”<<endl;}  
virtual   ~base()   {cout<<”base::~base()”<<endl;}  
};  
   
class   child:public base  
{  
public:  
child()   {cout<<”child::child()”<<endl;}  
/——————————————————————  
child(   const   child&   )   {   cout   <<   “child::child(   const   child&   )” <<   endl   ;   }     
——————————————————————-
/  
~child(){cout<<”child::~child()”<<endl;}  
virtual void show()const {cout<<”child::show()”<<endl;}  
int test(int i) {cout<<”int child::test(int)”<<endl;return 0;}  
};  
   
int   main()  
{  
   child   c1   ;  
   cout<<”—————————-“<<endl;  
   child   c2(c1);  
   cout<<”—————————-“<<endl;  
}  

输出为:

base::base()  
child::child()  
—————————-  
base::base(   const   base&)  

child::show()
—————————-  
child::~child()  
base::~base()  
child::~child()  
base::~base()  

2基类也没有显式定义自己的拷贝构造函数

#include <iostream>
using namespace std ;

class base
{
public:
base() { cout << “base::base()” << endl ; }
/base( const base& ) { cout << “base::base( const base&)” << endl ; } /
virtual ~base() { cout << “base::~base()” << endl ; }
};

class child : public base
{

public:
child() { cout << “child::child()” << endl ; }
/——————————————————————
child( const child& ) { cout << “child::child( const child& )” << endl ; }   
——————————————————————-
/
~child() { cout << “child::~child()” << endl ; }
int test(int i) { cout << “int child::test(int)” << endl ; return 0 ; }

};   
    
   int main()
{
child c1 ;
cout << “—————————-“ << endl ;
child c2(c1) ;
cout << “—————————-“ << endl ;
}  

输出结果:
base::base()
child::child()

—————————-   

child::~child()
base::~base()
child::~child()
base::~base()  

这时child c2(c1) ; 调用的是自己的默认拷贝构造函数,同时也会调用基类的默认构造函数(为什么是构造函数不是拷贝构造函数,下面的一种情况就会看到),既然默认的构造函数什么所以不会有什么输出信息。

3基类和派生类都显式定义了自己都拷贝构造函数

#include <iostream>
using namespace std ;

class base
{
public:
base() { cout << “base::base()” << endl ; }   
base( const base& ) { cout << “base::base( const base&)” << endl ; }   
virtual ~base() { cout << “base::~base()” << endl ; }
};

class child : public base
{

public:
child() { cout << “child::child()” << endl ; }   
child( const child& ) { cout << “child::child( const child& )” << endl ; }   
~child() { cout << “child::~child()” << endl ; }
int test(int i) { cout << “int child::test(int)” << endl ; return 0 ; }

};   
    
   int main()
{
child c1 ;
cout << “—————————-“ << endl ;
child c2(c1) ;
cout << “—————————-“ << endl ;
}  

输出为:

base::base()

child::child()

base::base()

child::child( const child& )

child::~child()
base::~base()
child::~child()
base::~base()  

注意这里child c2(c1) ;   调用了基类的构造函数而不是拷贝构造函数,这又是为什么呢?
请问拷贝构造函数是不是构造函数??    是,是个特殊的构造函数,但是他也是构造函数。C2调用自己的拷贝构造函数的时候,编译器只是认为你调用了构造函数,根据参数类型是调用我们所说的拷贝构造函数,但是当他决定调用基类的哪个构造函数的时候,他要看你传递给基类的参数了,你什么都没有给传递,当然调用默认构造函数了。

我想通过上面三种情况的讲解大家应该都能明白派生类拷贝构造函数的奥妙了,通过这个例子也对我学习C++的过程敲了下警钟:知识面还是不广,综合运用所学东西的能力还不够,尤其是那个多态的地方,如果是单独的多态可能一下子就看出来了,但是用在这里就傻眼了。

指针与数组

发表于 2006-08-28 | 分类于 C++

这是一个看似简单,但却不是那么容易搞清楚的问题。以下是在Andrew Koenig的杰作中看到的一些关于数组和指针的讲解,我只作了部分整理。
C语言中指针与数组这两个概念之间的联系是如此密不可分,以至于如果不能理解一个概念,就无法彻底理解另一个概念。C语言中的数组值得注意的地方有以下两点:
1:C语言只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来。
然而C语言中数组的元素可以是任何类型的对象,当然可以是另外一个数组,这样啊,
要”仿真”出一个多维数组就不是一件难事。(C99标准允许变长数组(VLA)。GCC编译器
实现了变长数组,但是细节与C99标准不完全一致。)
2:对于一个数组,我们只能够做两件事:确定该数组的大小,以及获得指向该数组下标为0的
元素的指针。其他关于数组的操作,哪怕他们看上去是以数组下标进行运算的,实际上都是
通过指针进行的。换句话说,任何一个数组下标运算都等同于一个对应的指针运算,因此我
们完全可以依据指针行为定义数组下标的行为。一旦我们彻底理解这两点以及他们所隐含
的意思,那么理解C语言的数组运算不过就是“小菜一碟”。对于指针加上一个整数,与给该
指针的二进制表示加上同样的整数,两者的含义截然不同,这点大家应该都是清楚的。

下面一个问题是关于指针加减问题的:
如果两个指针指向的是同一个数组中的元素,我们可以把这两个指针相减,这样是有意义的,例如:int *q=p+i;我们可以通过q-p而得到i的值。值得注意的是如果他们指向的不是同一个数组的元素,即使他们所指向的地址在内存中的位置正好间隔一个数组元素的整数倍,所得的结果仍然是无法保证其正确性的。至于指针相加的情况暂时好象没有发现有。
现在定义一个数组inta[3];如果我们在应该出现指针的地方,却采用了数组名来替换,那么
数组名就被当作指向该数组下标为0的元素的指针,这点大家也都很清楚。但是要注意一点,除了a被用做运算符sizeof的参数这一情形,在其他的所有情形中数组名a都代表指向该数组下标为0的元素的指针。正如我们合乎情理的期待,sizeof(a)的结果就是整个数组的大小,而不是指向该数组的元素的指针的大小。

下面来看看二维的情况(其他的都可以类推):
int ta[12[31];
int p;
int i;
因为ta是一个有着12个数组类型元素的数组,它的每个数组类型元素又是一个有着31个整形
元素的数组,所以ta[4]是数组ta的第5个元素,是ta数组中12个有着31个整形元素的数组之一。
因此,ta[4]的行为也就表现为一个有着31个整形元素的数组的行为。例如,sizeof(ta[4])的
结果是31与sizeof(int)的乘积。
又如:
p=ta[4];这个语句使指针p指向了数组ta[4]中下标为0的元素。
如果ta[4]是一个数组,我们当然可以通过下标的形式来指定这个数组中的元素,就像:
i=ta[4][7],与一维类似的道理,这个语句可以写成下面这样意思不变:
i=
(ta[4]+7);
或者:
i=((ta+4)+7);
但是可以看出第一种最简洁明了。
下面我们再看:
p=ta;
大家一看就知道这个语句是非法的,但具体原因是什么呢?
因为ta是一个二维数组,即“数组的数组”,在此处的上下文中使用ta名称会将其转换为
一个指向数组的指针;而p是一个指向整形变量的指针,这个语句试图将一种类型的指针
赋值给另一种类型的指针,所以是非法的。由于我们都很熟悉数组时针,即指向数组的指针,所以很快就会想到一个数组指针int (*p)[31];这时就可以这样赋值:p=ta;暂时就说到这里了。

C++头文件

发表于 2006-07-12 | 分类于 C++
学了两天 C++了,基础部分基本上看了一遍,用到一些编译器时发现里面的头文件不一样,有的有后缀名而有的没有后缀名。后来上网看了下才发现好象是标准C++头文件没有后缀名,以下是在网上找到的C++头文件一览: 

传统 C++

  #include <assert.h>    //设定插入点  

  #include <ctype.h>         //字符处理

  #include <errno.h>         //定义错误码

  #include <float.h>         //浮点数处理

  #include <fstream.h>       //文件输入/输出

  #include <iomanip.h>       //参数化输入/输出

  #include <iostream.h>   //数据流输入/输出

  #include <limits.h>    //定义各种数据类型最值常量

  #include <locale.h>    //定义本地化函数

  #include <math.h>     //定义数学函数

  #include <stdio.h>     //定义输入/输出函数

  #include <stdlib.h>    //定义杂项函数及内存分配函数

  #include <string.h>    //字符串处理

  #include <strstrea.h>   //基于数组的输入/输出

  #include <time.h>     //定义关于时间的函数

  #include <wchar.h>     //宽字符处理及输入/输出

  #include <wctype.h>    //宽字符分类

标准 C++ (同上的不再注释)

  #include <algorithm>    //STL 通用算法

  #include <bitset>     //STL 位集容器

  #include <cctype>

  #include <cerrno>

  #include <clocale>

  #include <cmath>

  #include <complex>     //复数类

  #include <cstdio>

  #include <cstdlib>

  #include <cstring>

  #include <ctime>

  #include <deque>      //STL 双端队列容器

  #include <exception>    //异常处理类

  #include <fstream>

  #include <functional>   //STL 定义运算函数(代替运算符)

  #include <limits>

  #include <list>      //STL 线性列表容器

  #include <map>       //STL 映射容器

  #include <iomanip>

  #include <ios>       //基本输入/输出支持

  #include <iosfwd>     //输入/输出系统使用的前置声明

  #include <iostream>

  #include <istream>     //基本输入流

  #include <ostream>     //基本输出流

  #include <queue>      //STL 队列容器

  #include <set>       //STL 集合容器

  #include <sstream>     //基于字符串的流

  #include <stack>      //STL 堆栈容器    

  #include <stdexcept>    //标准异常类

  #include <streambuf>    //底层输入/输出支持

  #include <string>     //字符串类

  #include <utility>     //STL 通用模板类

  #include <vector>     //STL 动态数组容器

  #include <cwchar>

  #include <cwctype>

  using namespace std;

C99 增加

   #include <complex.h>   //复数处理

   #include <fenv.h>    //浮点环境

   #include <inttypes.h>  //整数格式转换

   #include <stdbool.h>   //布尔环境

   #include <stdint.h>   //整型环境

   #include <tgmath.h>   //通用类型数学宏

学C++了

发表于 2006-07-11 | 分类于 C++

哈哈,终于开始学C++了,马上就是大三了,好像好多学校大一或者大二就开始学C++了,不能等到课堂上再学了,趁这个暑假一定要先看看。还好前面的语法部分基本上不用看,大一学过C,上学期数据结构又把C几乎是学了遍,后面的面向对象部分应该是这段时间的重要部分,幸运的是大二上学期又学过java(当时我也不知道学校为什么要把它当选修课)不过现在好像没什么印象了。我打算每天看三集视频教程,然后看下自己买的自认为经典的什么国外教程,至于上机练习什么时候心血来潮就上下,主要问题还是想玩会游戏,所以不知道一个暑假后自己能学到什么,期待中……

C中的字符数组

发表于 2006-05-07 | 分类于 C++

今天是五一的最后一天了,这个五一哪都没去,玩的也一般般,上午终于把那个数据结构的实验程序搞定了,说来真是惭愧,这么久了才弄完,主要是自己心思没用放在上面(这些天一直在玩游戏)。
这个实验程序大家应该都做过的就是那个魔王语言翻译的,通过这个程序又发现了自己程序设计的不少有待改进的地方,感受最深的是其中的字符数组使用的部分现在趁这个机会自己也好好总结下。
顾名思义字符数组就是用来存放字符数据的数组,其中一个元素存放一个字符。光这样说太枯燥了来看看这个程序吧:

#include <stdio.h>

#include <conio.h>
main()
{
char a[10],b[10];
int i;
clrscr();
gets(a);
for(i=0;a[i]!=’/0’;i++)
b[i]=a[i];
printf(“i=%d,blen=%d/n”,i,strlen(b));
puts(b);
getch();
}
大家都可以看出这是一个非常简单的程序,首先你认为这个程序有没有问题呢?当然这个程序的语法是没有任何错误的,但运行后的结果会是怎么样的呢?以下是我在tc2.0下的运行结果:
journey
i=7,blen=15
journey????????
上面的8个?是代表的乱码,为什么结果会是这样的呢,而且blen为什么是15呢?如果记得C中的字符数组的存放状态就不难理解了,C语言规定了一个字符串结束标志,以字符‘/0’代表。如果一个字符串,其中的第1个字符为‘/0’,则此字符串的有效字符为9个,也就是说,在遇到字符‘/0’,表示字符串结束,由它前面的字符组成字符串。系统对字符串常量自动加一个‘/0’作为结束符。‘/0’代表ASCII码为0的字符,从ASCII码表中可以查到,ASCII码为0的字符不是一个可以显示的字符,而是一个“空操作符”,即它什么也不干。用它来作为字符串结束标志不会产生附加的操作或增加有效字符,只起一个供辨别的标志。
上面程序中的字符数组a有标准输入终端输入系统会自动的加上一个‘/0’,而数组b是由a赋值的但是a并没有把‘/0’赋给它,由于它没有经过终端进行输入,系统是不会知道它什么时候该结束,所以不会自动给它加上,因此产生了上面的结果。如果在程序自己加上b[i]=’/0’,结果就是正确的了。

新年希望

发表于 2006-01-29 | 分类于 生活轨迹

真是快,总是这么说难免有点俗,可是这又是最真实的,转眼我要进入大二下了,学了这么久的计算机,我不知道学到了什么,虽然身边的同学都差不多,但在网上才发现我什么也没学到,大一上就学了C。现在连个简单的运行错误都不知道,真是惭愧,还好幸亏我发现了这个网站而且我的第一个在网上的问题就是在这里得到答案的,我并不是来评价这个网站,只是高兴我以后可以找到学习的目标了,还有昨天在网上看到我们学校的那个得了中国编程大赛的冠军,太棒了,也对我们学校更加自信了,现在正在温习C希望不仅可以提高自己的编程能力,也为以后的C++做好开头,希望2006里好运降临到我的头上,说笑的,他妈的什么不都是靠自己争取的吗?

1…1516

billowqiu

157 日志
33 分类
10 标签
GitHub E-Mail
© 2020 billowqiu
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.3