C++中如何在非类函数中调用类函数?

soul24k 2015-05-21 03:20:14
例如我现在定义了一个线程函数
void ThreadProc() //线程函数
{
CTime time;
CString strTime;
m_bRun = TRUE;//运行标志
while (m_bRun)
{
time = CTime::GetCurrentTime();
strTime = time.Format("%H:%M:%S");
GetDlgItem( IDC_TIME)->SetWindowText(strTime );//IDC_TIME属于CXXXDlg类
Sleep(1000);
}
}
如果在void CXXXDlg::ThreadProc() 是可以直接调用GetDlgItem的,请问这个该如何调用?
...全文
957 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_35003152 2016-11-02
  • 打赏
  • 举报
回复
引用 10 楼 soul24k 的回复:
[quote=引用 6 楼 Saleayas 的回复:] 简单的说,你的主程序用来做界面显示。 现在你创建一个线程去查询一个设备或远程的数据更新。 当线程等待到有数据更新时,比如说,你的线程检测到时间过来1秒,需要改变时, 此时,不应该呼叫主界面来修改显示数据。而是应该向主界面发送一个消息, 然后,主界面自己处理这个消息,比如修改显示的时间。
谢谢,这样方式确实是比较好的,我试了可以从线程发消息给到主程序的窗口的,例如: CString str; str = "thanks"; HWND hWnd = AfxGetMainWnd()->GetSafeHwnd(); SendMessage(nHwnd, WM_MAIN_MSG, (WPARAM)&str, NULL);//WM_MAIN_MSG在主程序当中的消息宏 以下为主程序那边的消息接收函数: afx_msg LRESULT CReadADlg::OnMainMsg(WPARAM wParam, LPARAM lParam) { CString* strMsg = (CString*)wParam; SetDlgItemText(IDC_EDIT1, *strMsg); AfxMessageBox(*strMsg, MB_OK); return 0; } 但我还有一个问题就是线程当中,如何接收主程序发过来的消息,目前我是使用全局变量来传递。[/quote] 为什么我的WM_MAIN_MSG这个后面不能跟逗号?说是语法错误,应输入表达式。你WM_MAIN_MSG怎么声明的
Saleayas 2015-05-26
  • 打赏
  • 举报
回复
不要使用 HWND hWnd = AfxGetMainWnd()->GetSafeHwnd(); 来获取主线程的窗口,把主线程的窗口作为 CreateThread 时的参数传入。 如果子线程也需要处理消息,那么添加 while(GetMessage()) 这个消息循环。 一般来说,子线程是不需要处理消息,因为这样的子线程往往是自己循环等待的。 如果是消息返回,那么很简单,直接等待 SendMessage 函数返回。
lx624909677 2015-05-26
  • 打赏
  • 举报
回复
先获得控件所在窗口的句柄
soul24k 2015-05-25
  • 打赏
  • 举报
回复
引用 8 楼 asqw5104481 的回复:
[quote=引用 7 楼 asqw5104481 的回复:] 在创建线程的时候把你对话框的this指针传进去就好了 hThread = CreateThread(NULL,0, ThreadProc,this,0,NULL); void ThreadProc(LPVOID lpParam) //线程函数 { testDlg *threadtest = (testDlg *)lpParam; .......... .......... threadtest ->( IDC_TIME)->SetWindowText(strTime ); }
threadtest ->GetDlgItem( IDC_TIME)->SetWindowText(strTime ); 不好意思,最后一句弄错了[/quote] 谢谢,你的这个方式可以也实现我的功能。
soul24k 2015-05-25
  • 打赏
  • 举报
回复
引用 6 楼 Saleayas 的回复:
简单的说,你的主程序用来做界面显示。 现在你创建一个线程去查询一个设备或远程的数据更新。 当线程等待到有数据更新时,比如说,你的线程检测到时间过来1秒,需要改变时, 此时,不应该呼叫主界面来修改显示数据。而是应该向主界面发送一个消息, 然后,主界面自己处理这个消息,比如修改显示的时间。
谢谢,这样方式确实是比较好的,我试了可以从线程发消息给到主程序的窗口的,例如: CString str; str = "thanks"; HWND hWnd = AfxGetMainWnd()->GetSafeHwnd(); SendMessage(nHwnd, WM_MAIN_MSG, (WPARAM)&str, NULL);//WM_MAIN_MSG在主程序当中的消息宏 以下为主程序那边的消息接收函数: afx_msg LRESULT CReadADlg::OnMainMsg(WPARAM wParam, LPARAM lParam) { CString* strMsg = (CString*)wParam; SetDlgItemText(IDC_EDIT1, *strMsg); AfxMessageBox(*strMsg, MB_OK); return 0; } 但我还有一个问题就是线程当中,如何接收主程序发过来的消息,目前我是使用全局变量来传递。
C_码农 2015-05-23
  • 打赏
  • 举报
回复
引用 7 楼 asqw5104481 的回复:
在创建线程的时候把你对话框的this指针传进去就好了 hThread = CreateThread(NULL,0, ThreadProc,this,0,NULL); void ThreadProc(LPVOID lpParam) //线程函数 { testDlg *threadtest = (testDlg *)lpParam; .......... .......... threadtest ->( IDC_TIME)->SetWindowText(strTime ); }
threadtest ->GetDlgItem( IDC_TIME)->SetWindowText(strTime ); 不好意思,最后一句弄错了
C_码农 2015-05-23
  • 打赏
  • 举报
回复
在创建线程的时候把你对话框的this指针传进去就好了 hThread = CreateThread(NULL,0, ThreadProc,this,0,NULL); void ThreadProc(LPVOID lpParam) //线程函数 { testDlg *threadtest = (testDlg *)lpParam; .......... .......... threadtest ->( IDC_TIME)->SetWindowText(strTime ); }
paschen 2015-05-23
  • 打赏
  • 举报
回复
DWORD ThreadProc(LPVOID lpParameter) 其中的参数是空传入你这个类的指针,(LPVOID)this 在线程函数中调用时,将指针转换成你的类的类型,然后用指针调用其函数
Saleayas 2015-05-22
  • 打赏
  • 举报
回复
简单的说,你的主程序用来做界面显示。 现在你创建一个线程去查询一个设备或远程的数据更新。 当线程等待到有数据更新时,比如说,你的线程检测到时间过来1秒,需要改变时, 此时,不应该呼叫主界面来修改显示数据。而是应该向主界面发送一个消息, 然后,主界面自己处理这个消息,比如修改显示的时间。
soul24k 2015-05-22
  • 打赏
  • 举报
回复
引用 2 楼 maguiwa 的回复:
你的问题实际是一个新建的线程想用调用UI线程(主线程)的资源(IDC_TIME 不知道是啥控件); 你只要在CreateThread 或者afxBeginThread中传入带有主线程this指针的参数就行。或者你把包含 IDC_TIME资源的线程的this指针弄成全局的。
不好意思,没有讲清楚 IDC_TIME就是一个编辑框,我想用来放一个时间显示 请问如何将指针传进行,查了很久没有找到答案。 谢谢!
mfmfmmf1 2015-05-21
  • 打赏
  • 举报
回复
非类对象调用类函数?可以啊,但只能调用类的静态函数 比如CTime::GetCurrentTime()
worldy 2015-05-21
  • 打赏
  • 举报
回复
void ThreadProc() //线程函数 能这样?函数的参数呢?事实上,可以通过参数将对象指针传递进去
酱油党 2015-05-21
  • 打赏
  • 举报
回复
你的问题实际是一个新建的线程想用调用UI线程(主线程)的资源(IDC_TIME 不知道是啥控件); 你只要在CreateThread 或者afxBeginThread中传入带有主线程this指针的参数就行。或者你把包含 IDC_TIME资源的线程的this指针弄成全局的。
relaxisland 2015-05-21
  • 打赏
  • 举报
回复
什么意思? 你只要有一个类的变量,就可以调用它的成员函数啊
主体:(一) 一、C++概述 (一) 发展历史 1980年,Bjarne Stroustrup博士开始着手创建一种模拟语言,能够具有面向对象的程序设计特色。在当时,面向对象编程还是一个比较新的理念,Stroustrup博士并不是从头开始设计新语言,而是在C语言的基础上进行创建。这就是C++语言。 1985年,C++开始在外面慢慢流行。经过多年的发展,C++已经有了多个版本。为次,ANSI和ISO的联合委员会于1989年着手为C++制定标准。1994年2月,该委员会出版了第一份非正式草案,1998年正式推出了C++的国际标准。 (二) C和C++ C++是C的超集,也可以说C是C++的子集,因为C先出现。按常理说,C++编译器能够编译任何C程序,但是C和C++还是有一些小差别。 例如C++增加了C不具有的关键字。这些关键字能作为函数和变量的标识符在C程序使用,尽管C++包含了所有的C,但显然没有任何C++编译器能编译这样的C程序。 C程序员可以省略函数原型,而C++不可以,一个不带参数的C函数原型必须把void写出来。而C++可以使用空参数列表。 C++new和delete是对内存分配的运算符,取代了C的malloc和free。 标准C++的字符串取代了C标准C函数库头文件的字符数组处理函数C++用来做控制态输入输出的iostream库替代了标准C的stdio函数库。 C++的try/catch/throw异常处理机制取代了标准C的setjmp()和longjmp()函数。 二、关键字和变量 C++相对与C增加了一些关键字,如下: typename bool dynamic_cast mutable namespace static_cast using catch explicit new virtual operator false private template volatile const protected this wchar_t const_cast public throw friend true reinterpret_cast try bitor xor_e and_eq compl or_eq not_eq bitand 在C++还增加了bool型变量和wchar_t型变量: 布尔型变量是有两种逻辑状态的变量,它包含两个值:真和假。如果在表达式使用了布尔型变量,那么将根据变量值的真假而赋予整型值1或0。要把一个整型变量转换成布尔型变量,如果整型值为0,则其布尔型值为假;反之如果整型值为非0,则其布尔型值为真。布儿型变量在运行时通常用做标志,比如进行逻辑测试以改变程序流程。 #include iostream.h int main() { bool flag; flag=true; if(flag) cout<C++还包括wchar_t数据型,wchar_t也是字符型,但是是那些宽度超过8位的数据型。许多外文字符集所含的数目超过256个,char字符型无法完全囊括。wchar_t数据型一般为16位。 标准C++的iostream包括了可以支持宽字符的和对象。用wout替代cout即可。 #include iostream.h int main() { wchar_t wc; wc='b'; wout<类型)。 三、强制型转换 有时候,根据表达式的需要,某个数据需要被当成另外的数据型来处理,这时,就需要强制编译器把变量或常数由声明时的型转换成需要的型。为此,就要使用强制型转换说明,格式如下: int* iptr=(int*) &table; 表达式的前缀(int*)就是传统C风格的强制型转换说明(typecast),又可称为强制转换说明(cast)。强制转换说明告诉编译器把表达式转换成指定的型。有些情况下强制转换是禁用的,例如不能把一个结构型转换成其他任何型。数字型和数字型、指针和指针之间可以相互转换。当然,数字型和指针型也可以相互转换,但通常认为这样做是不安全而且也是没必要的。强制型转换可以避免编译器的警告。 long int el=123; short i=(int) el; float m=34.56; int i=(int) m; 上面两个都是C风格的强制型转换,C++还增加了一种转换方式,比较一下上面和下面这个书写方式的不同: long int el=123; short i=int (el); float m=34.56; int i=int (m); 使用强制型转换的最大好处就是:禁止编译器对你故意去做的事发出警告。但是,利用强制型转换说明使得编译器的型检查机制失效,这不是明智的选择。通常,是不提倡进行强制型转换的。除非不可避免,如要调用malloc()函数时要用的void型指针转换成指定型指针。 四、标准输入输出流 在C语言,输入输出是使用语句scanf()和printf()来实现的,而C++是使用来实现的。 #include iostream.h main() //C++main()函数默认为int型,而C语言默认为void型。 { int a; cout<>a; /*输入一个数值*/ cout<C++语言的组成部分。虽然他们已经是ANSI标准C++被定义,但是他们不是语言的内在组成部分。在C++不提供内在的输入输出运算符,这与其他语言是不同的。输入和输出是通过C++来实现的,cin和cout是这些的实例,他们是在C++语言的外部实现。 在C++语言,有了一种新的注释方法,就是‘//’,在该行//后的所有说明都被编译器认为是注释,这种注释不能换行。C++仍然保留了传统C语言的注释风格/*……*/。 C++也可采用格式化输出的方法: #include iostream.h int main() { int a; cout<>a; cout<中出现。 五、函数参数问题 (一) 无名的函数形参 声明函数时可以包含一个或多个用不到的形式参数。这种情况多出现在用一个通用的函数指针调用多个函数的场合,其有些函数不需要函数指针声明的所有参数。看下面的例子: int fun(int x,int y) { return x*2; } 尽管这样的用法是正确的,但大多数C和C++的编译器都会给出一个警告,说参数y在程序没有被用到。为了避免这样的警告,C++允许声明一个无名形参,以告诉编译器存在该参数,且调用者需要为其传递一个实际参数,但是函数不会用到这个参数。下面给出使用了无名参数的C++函数代码: int fun(int x,int) //注意不同点 { return x*2; } (二) 函数的默认参数 C++函数的原型可以声明一个或多个带有默认值的参数。如果调用函数时,省略了相应的实际参数,那么编译器就会把默认值作为实际参数。可以这样来声明具有默认参数的C++函数原型: #include iostream.h void show(int=1,float=2.3,long=6); int main() { show(); show(2); show(4,5.6); show(8,12.34,50L); return 0; } void show(int first,float second,long third) { cout<中,第一次调用show()函数时,让编译器自动提供函数原型指定的所有默认参数,第二次调用提供了第一个参数,而让编译器提供剩下的两个,第三次调用则提供了前面两个参数,编译器只需提供最后一个,最后一个调用则给出了所有三个参数,没有用到默认参数。 六、函数重载 在C++,允许有相同的函数名,不过它们的参数型不能完全相同,这样这些函数就可以相互区别开来。而这在C语言是不允许的。 1.参数个数不同 #include iostream.h void a(int,int); void a(int); int main() { a(5); a(6,7); return 0; } void a(int i) { cout<a; for(int i=1;i<=10;i++) //C语言,不允许在这里定义变量 { static int a=0; //C语言,同一函数块,不允许有同名变量 a+=i; cout<<::a<< <C++语言,仍然支持malloc()和free()来分配和释放内存,同时增加了new和delete来管理内存。 1.为固定大小的数组分配内存 #include iostream.h int main() { int *birthday=new int[3]; birthday[0]=6; birthday[1]=24; birthday[2]=1940; cout<size; int *array=new int[size]; for(int i=0;iC++,引用是一个经常使用的概念。引用型变量是其他变量的一个别名,我们可以认为他们只是名字不相同,其他都是相同的。 1.引用是一个别名 C++的引用是其他变量的别名。声明一个引用型变量,需要给他一个初始化值,在变量的生存周期内,该值不会改变。& 运算符定义了一个引用型变量: int a; int& b=a; 先声明一个名为a的变量,它还有一个别名b。我们可以认为是一个人,有一个真名,一个外号,以后不管是喊他a还是b,都是叫他这个人。同样,作为变量,以后对这两个标识符操作都会产生相同的效果。 #include iostream.h int main() { int a=123; int& b=a; cout<函数没有明显区别,不过他们所花的时间却有很大差异,func2()函数所用的时间开销会比func2()函数少很多。它们还有一个差别,如果程序递归func1(),随着递归的深入,会因为栈的耗尽而崩溃,但func2()没有这样的担忧。 4.以引用方式调用函数把引用作为参数传递给另一个函数时,被调用函数将直接对参数在调用的拷贝进行操作,而不是产生一个局部的拷贝(传递变量本身是这样的)。这就称为以引用方式调用。把参数的值传递到被调用函数内部的拷贝则称为以传值方式调用。 #include iostream.h void display(const Date&,const char*); void swapper(Date&,Date&); struct Date { int month,day,year; }; int main() { static Date now={2,23,90}; static Date then={9,10,60}; display(now,Now: ); display(then,Then: ); swapper(now,then); display(now,Now: ); display(then,Then: ); return 0; } void swapper(Date& dt1,Date& dt2) { Date save; save=dt1; dt1=dt2; dt2=save; } void display(const Date& dt,const char *s) { cout<dt; if(dt>0 && dt<4) { const Date& bd=getdate(dt); cout<类的设计,构造函数和析构函数 是编程人员表达自定义数据型的C++机制。它和C语言的结构似,C++支持数据抽象和面向对象的程序设计,从某种意义上说,也就是数据型的设计和实现。 一、的设计 1.的声明 class 名 { private: //私有 ... public: //公有 ... }; 2.的成员 一般在C++,所有定义的变量和函数都是的成员。如果是变量,我们就叫它数据成员如果是函数,我们就叫它成员函数。 3.成员的可见性 private和public访问控制符决定了成员的可见性。由一个访问控制符设定的可访问状态将一直持续到下一个访问控制符出现,或者声明的结束。私有成员仅能被同一个的成员函数访问,公有成员既可以被同一的成员函数访问,也可以被其他已经实例化的函数访问。当然,这也有例外的情况,这是以后要讨论的友元函数默认的数据型是private,结构的默认型是public。一般情况下,变量都作为私有成员出现,函数都作为公有成员出现。 还有一种访问控制符protected,叫保护成员,以后再说明。 4.初始化 在声明一个的对象时,可以用圆括号()包含一个初始化表。 看下面一个例子: #include iostream.h class Box { private: int height,width,depth; //3个私有数据成员 public: Box(int,int,int); ~Box(); int volume(); //成员函数 }; Box::Box(int ht,int wd,int dp) { height=ht; width=wd; depth=dp; } Box::~Box() { //nothing } int Box::volume() { return height*width*depth; } int main() { Box thisbox(3,4,5); //声明一个对象并初始化 cout<没有private成员和protected成员时,也没有虚函数,并且不是从其他派生出来的,可以用{}来初始化。(以后再讲解) 5.内联函数 内联函数和普通函数的区别是:内联函数是在编译过程展开的。通常内联函数必须简短。定义的内联函数有两种方法:一种和C语言一样,在定义函数时使用关键字inline。如: inline int Box::volume() { return height*width*depth; } 还有一种方法就是直接在声明的内部定义函数体,而不是仅仅给出一个函数原型。我们把上面的函数简化一下: #include iostream.h class Box { private: int height,width,depth; public: Box(int ht,int wd,int dp) { height=ht; width=wd; depth=dp; } ~Box(); int volume() { return height*width*depth; } }; int main() { Box thisbox(3,4,5); //声明一个对象并初始化 cout<函数都默认为内联函数了。 二、构造函数 什么是构造函数?通俗的讲,在函数名和名相同的函数称为构造函数。上面的Box()函数就是构造函数C++允许同名函数,也就允许在一个有多个构造函数。如果一个都没有,编译器将为该产生一个默认的构造函数,这个构造函数可能会完成一些工作,也可能什么都不做。 绝对不能指定构造函数型,即使是void型都不可以。实际上构造函数默认为void型。 当一个的对象进入作用域时,系统会为其数据成员分配足够的内存,但是系统不一定将其初始化。和内部数据型对象一样,外部对象的数据成员总是初始化为0。局部对象不会被初始化。构造函数就是被用来进行初始化工作的。当自动型的对象离开其作用域时,所站用的内存将释放回系统。 看上面的例子,构造函数Box()函数接受三个整型擦黑素,并把他们赋值给立方体对象的数据成员。 如果构造函数没有参数,那么声明对象时也不需要括号。 1.使用默认参数的构造函数 当在声明对象时,如果没有指定参数,则使用默认参数来初始化对象。 #include iostream.h class Box { private: int height,width,depth; public: Box(int ht=2,int wd=3,int dp=4) { height=ht; width=wd; depth=dp; } ~Box(); int volume() { return height*width*depth; } }; int main() { Box thisbox(3,4,5); //初始化 Box defaulbox; //使用默认参数 cout<函数 没有参数或者参数都是默认值的构造函数称为默认构造函数。如果你不提供构造函数,编译器会自动产生一个公共的默认构造函数,这个构造函数什么都不做。如果至少提供一个构造函数,则编译器就不会产生默认构造函数。 3.重载构造函数 一个可以有多个构造函数。这些构造函数必须具有不同的参数表。在一个需要接受不同初始化值时,就需要编写多个构造函数,但有时候只需要一个不带初始值的空的Box对象。 #include iostream.h class Box { private: int height,width,depth; public: Box() { //nothing } Box(int ht=2,int wd=3,int dp=4) { height=ht; width=wd; depth=dp; } ~Box(); int volume() { return height*width*depth; } }; int main() { Box thisbox(3,4,5); //初始化 Box otherbox; otherbox=thisbox; cout<函数一个没有初始化值,一个有。当没有初始化值时,程序使用默认值,即2,3,4。 但是这样的程序是不好的。它允许使用初始化过的和没有初始化过的Box对象,但它没有考虑当thisbox给otherbox赋值失败后,volume()该返回什么。较好的方法是,没有参数表的构造函数也把默认值赋值给对象。 class Box { int height,width,depth; public: Box() { height=0;width=0;depth=0; } Box(int ht,int wd,int dp) { height=ht;width=wd;depth=dp; } int volume() { return height*width*depth; } }; 这还不是最好的方法,更好的方法是使用默认参数,根本不需要不带参数的构造函数。 class Box { int height,width,depth; public: Box(int ht=0,int wd=0,int dp=0) { height=ht;width=wd;depth=dp; } int volume() { return height*width*depth; } }; 三、析构函数 当一个的对象离开作用域时,析构函数将被调用(系统自动调用)。析构函数的名字和名一样,不过要在前面加上 ~ 。对一个来说,只能允许一个析构函数,析构函数不能有参数,并且也没有返回值。析构函数的作用是完成一个清理工作,如释放从堆分配的内存。 我们也可以只给出析构函数的形式,而不给出起具体函数体,其效果是一样的,如上面的例子。但在有些情况下,析构函数又是必需的。如在从堆分配了内存,则必须在析构函数释放 主体:(三)的转换 C++的内部数据型遵循隐式型转换规则。假设某个表达市使用了一个短整型变量,而编译器根据上下文认为这儿需要是的长整型,则编译器就会根据型转换规则自动把它转换成长整型,这种隐式转换出现在赋值、参数传递、返回值、初始化和表达式。我们也可以为提供相应的转换规则。 对一个建立隐式转换规则需要构造一个转换函数,该函数作为的成员,可以把该的对象和其他数据型的对象进行相互转换。声明了转换函数,就告诉了编译器,当根据句法判定需要型转换时,就调用函数。 有两种转换函数。一种是转换构造函数;另一种是成员转换函数。需要采用哪种转换函数取决于转换的方向。 一、转换构造函数 当一个构造函数仅有一个参数,且该参数是不同于该的一个数据型,这样的构造函数就叫转换构造函数。转换构造函数把别的数据型的对象转换为该的一个对象。和其他构造函数一样,如果声明的对象的初始化表同转换构造函数的参数表相匹配,该函数就会被调用。当在需要使用该的地方使用了别的数据型,便宜器就会调用转换构造函数进行转换。 #include iostream.h #include time.h #include stdio.h class Date { int mo, da, yr; public: Date(time_t); void display(); }; void Date::display() { char year[5]; if(yr<10) sprintf(year,0%d,yr); else sprintf(year,%d,yr); cout<tm_mon+1; yr=tim->tm_year; if(yr>=100) yr-=100; } int main() { time_t now=time(0); Date dt(now); dt.display(); return 0; } 本程序先调用time()函数来获取当前时间,并把它赋给time_t对象;然后程序通过调用Date的转换构造函数来创建一个Date对象,该对象由time_t对象转换而来。time_t对象先传递给localtime()函数,然后返回一个指向tm结构(time.h文件声明)的指针,然后构造函数把结构的日月年的数值拷贝给Date对象的数据成员,这就完成了从time_t对象到Date对象的转换。 二、成员转换函数 成员转换函数把该的对象转换为其他数据型的对象。在成员转换函数的声明要用到关键字operator。这样声明一个成员转换函数: operator aaa(); 在这个例子,aaa就是要转换成的数据型的说明符。这里的型说明符可以是任何合法的C++型,包括其他的。如下来定义成员转换函数; Classname::operator aaa() 名标识符是声明了该函数型说明符。上面定义的Date并不能把该的对象转换回time_t型变量,但可以把它转换成一个长整型值,计算从2000年1月1日到现在的天数。 #include iostream.h class Date { int mo,da,yr; public: Date(int m,int d,int y) {mo=m; da=d; yr=y;} operator int(); //声明 }; Date::operator int() //定义 { static int dys[]={31,28,31,30,31,30,31,31,30,31,30,31}; int days=yr-2000; days*=365; days+=(yr-2000)/4; for(int i=0;i类的转换 上面两个例子都是C++对象和内部数据对象之间的相互转换。也可以定义转换函数来实现两个对象之间的相互转换。 #include iostream.h class CustomDate { public: int da, yr; CustomDate(int d=0,int y=0) {da=d; yr=y;} void display() { cout<函数 operator CustomDate(); //成员转换函数 void display() { cout<调用成员转换函数 cd.display(); dt = cd; //调用转换构造函数 dt.display(); return 0; } 这个例子有两个CustomDate和Date,CustomDate型日期包含年份和天数。 这个例子没有考虑闰年情况。但是在实际构造一个时,应该考虑到所有问题的可能性。 在Date里具有两种转换函数,这样,当需要从Date型变为CustomDate型十,可以调用成员转换函数;反之可以调用转换构造函数。 不能既在Date定义成员转换函数,又在CustomDate里定义转换构造函数。那样编译器在进行转换时就不知道该调用哪一个函数,从而出错。 四、转换函数调用 C++调用转换函数有三种形式:第一种是隐式转换,例如编译器需要一个Date对象,而程序提供的是CustomDate对象,编译器会自动调用合适的转换函数。另外两种都是需要在程序代码明确给出的显式转换。C++强制型转换是一种,还有一种是显式调用转换构造函数和成员转换函数。下面的程序给出了三转换形式: #include iostream.h class CustomDate { public: int da, yr; CustomDate(int d=0,int y=0) {da=d; yr=y;} void display() { cout<类型对象之间的相互赋值来调用转换函数,还有几种调用的可能: 参数传递 初始化 返回值 表达式语句 这些情况下,都有可能调用转换函数。 下面的程序不难理解,就不分析了。 #include iostream.h class CustomDate { public: int da, yr; CustomDate() {} CustomDate(int d,int y) { da=d; yr=y;} void display() { cout<函数 注意上面Tester的构造函数前面有一个explicit修饰符。如果不加上这个关键字,那么在需要把CustomDate对象转换成Tester对象时,编译器会把该函数当作转换构造函数调用。但是有时候,并不想把这种只有一个参数的构造函数用于转换目的,而仅仅希望用它来显式地初始化对象,此时,就需要在构造函数前加explicit。如果在声明了Tester对象以后使用了下面的语句将导致一个错误: ts=jd; //error 这个错误说明,虽然Tester有一个以Date型变量为参数的构造函数,编译器却不会把它看作是从Date到Tester的转换构造函数,因为它的声明包含了explicit修饰符。 七、表达式内部的转换 在表达式内部,如果发现某个型和需要的不一致,就会发生错误。数字型的转换是很简单,这里就不举例了。下面的程序是把Date对象转换成长整型值。 #include iostream.h class Date { int mo, da, yr; public: Date(int m,int d,int y) { mo=m; da=d; yr=y; } operator long(); }; Date::operator long() { static int dys[]={31,28,31,30,31,30,31,31,30,31,30,31}; long days=yr; days*=365; days+=(yr-1900)/4; //从1900年1月1日开始计算 for(int i=0;i中,当需要转换的对象可以转换成某个数字型,或者表达式调用了作用于某个的重载运算符时,就会发生隐式转换。运算符重载以后再学习。 主体:(四)私有数据成员和友元 一、私有数据成员的使用 1.取值和赋值成员函数 面向对象的约定就是保证所有数据成员的私有性。一般我们都是通过公有成员函数来作为公共接口来读取私有数据成员的。某些时候,我们称这样的函数为取值和赋值函数。 取值函数的返回值和传递给赋值函数的参数不必一一匹配所有数据成员的型。 #include iostream.h class Date { int mo, da, yr; public: Date(int m,int d,int y) { mo=m; da=d; yr=y; } int getyear() const { return yr; } void setyear(int y) { yr = y; } }; int main() { Date dt(4,1,89); cout<函数来访问和改变的数据。这样有利于软件的设计和维护。比如,改变Date内部数据的形式,但仍然用修改过的getyear()和setyear()来提供访问接口,那么使用该就不必修改他们的代码,仅需要重新编译程序即可。 2.常量成员函数 注意上面的程序getyear()被声明为常量型,这样可以保证该成员函数不会修改调用他的对象。通过加上const修饰符,可以使访问对象数据的成员函数仅仅完成不会引起数据变动的那些操作。 如果程序声明某个Date对象为常量的话,那么该对象不得调用任何非常量型成员函数,不论这些函数是否真的试图修改对象的数据。只有把那些不会引起数据改变的函数都声明为常量型,才可以让常量对象来调用。 3.改进的成员转换函数 下面的程序改进了从Date对象到CustomDate对象的成员转换函数,用取值和赋值函数取代了使用公有数据成员的做法。(以前的程序代码在上一帖) #include iostream.h class CustomDate { int da,yr; public: CustomDate() {} CustomDate(int d,int y) { da=d; yr=y; } void display() const {cout<中Date::operator CustomDate()声明为常量型,因为这个函数没有改变调用它对象的数据,尽管它修改了一个临时CustomDate对象并将其作为函数返回值。 二、友元 前面已经说过了,私有数据成员不能被外的其他函数读取,但是有时候会允许一些特殊的函数直接读写其私有数据成员。 关键字friend可以让特定的函数或者别的的所有成员函数对私有数据成员进行读写。这既可以维护数据的私有性,有可以保证让特定的函数能够直接访问私有数据。 1.友元 一个可以声明另一个为其友元,这个友元的所有成员函数都可以读写它的私有数据。 #include iostream.h class Date; class CustomDate { int da,yr; public: CustomDate(int d=0,int y=0) { da=d; yr=y; } void display() const {cout<中,有这样一句 friend Date; 该语句告诉编译器,Date的所有成员函数有权访问CustomDate的私有成员。因为Date的转换函数需要知道CustomDate的每个数据成员,所以真个Date都被声明为CustomDate的友元。 2.隐式构造函数 上面程序对CustomDate的构造函数调用私有显示该需要如下的一个转换构造函数: CustomDate(Date& dt); 但是唯一的一个构造函数是:CustomDate(int d=0;int y=0); 这就出现了问题,编译器要从Date对象构造一个CustomDate对象,但是CustomDate并没有定义这样的转换构造函数。不过Date定义了一个成员转换函数,它可以把Date对象转换成CustomDate对象。于是编译器开始搜索CustomDate,看其是否有一个构造函数,能从一个已存在的CustomDate的对象创建新的CustomDate对象。这种构造函数叫拷贝构造函数。拷贝构造函数也只有一个参数,该参数是它所属的的一个对象,由于CustomDate没有拷贝构造函数,于是编译器就会产生一个默认的拷贝构造函数,该函数简单地把已存在的对象的每个成员拷贝给新对象。现在我们已经知道,编译器可以把Date对象转换成CustomDate对象,也可以从已存在的CustomDate对象生成一个新的CustomDate对象。那么上面提出的问题,编译器就是这样做的:它首先调用转换函数,从Date对象创建一个隐藏的、临时的、匿名的CustomDate对象,然后用该临时对象作为参数调用默认拷贝构造函数,这就生成了一个新的CustomDate对象。 3.预引用 上面的例子还有这样一句 class Date; 这个语句叫做预引用。它告诉编译器,Date将在后面定义。编译器必须知道这个信号,因为CustomDate引用了Date,而Date里也引用了CustomDate,必须首先声明其之一。 使用了预引用后,就可以声明未定义的的友元、指针和引用。但是不可以使用那些需要知道预引用的的定义细节的语句,如声明该的一个实例或者任何对该成员的引用。 4.显式友元预引用 也可以不使用预引用,这只要在声明友元的时候加上关键自class就行了。 #include iostream.h class CustomDate { int da,yr; public: CustomDate(int d=0,int y=0) { da=d; yr=y; } void display() const {cout<函数 通常,除非真的需要,否则并不需要把整个都设为另一个的友元,只需挑出需要访问当前私有数据成员的成员函数,将它们设置为该的友元即可。这样的函数称为友元函数。 下面的程序限制了CustomDate数据成员的访问,Date只有需要这些数据的成员函数才有权读写它们。 #include iostream.h class CustomDate; class Date { int mo,da,yr; public: Date(const CustomDate&); void display() const {cout<函数Date对象调用CustomDate的构造函数创建了一个匿名CustomDate对象,然后用该对象创建了一个Date对象。这种用法在C++是经常出现的。 7.成员的友元函数 有时候友元函数未必是某个的成员。这样的函数拥有对象私有数据成员的读写权,但它并不是任何的成员函数。这个特性在重载运算符时特别有用。 成员的友元函数通常被用来做为之间的纽带。一个函数如果被两个同时声明为友元,它就可以访问这两个的私有成员。下面的程序说明了一个可以访问两个私有数据成员的友元函数是如何将在两个之间架起桥梁的。 #include iostream.h class Time; class Date { int mo,da,yr; public: Date(int m,int d,int y) { mo=m; da=d; yr=y;} friend void display(const Date&, const Time&); }; class Time { int hr,min,sec; public: Time(int h,int m,int s) { hr=h; min=m; sec=s;} friend void display(const Date&, const Time&); }; void display(const Date& dt, const Time& tm) { cout << dt.mo << '/' << dt.da << '/' << dt.yr; cout << ' '; cout << tm.hr << ':' << tm.min << ':' << tm.sec; } int main() { Date dt(2,16,97); Time tm(10,55,0); display(dt, tm); return 0; } 主体:(五)析构函数和this指针 一、析构函数 前面的一些例子都没有说明析构函数,这是因为所用到的在结束时不需要做特别的清理工作。下面的程序给出了一新的Date,其包括一个字符串指针,用来表示月份。 #include iostream.h #include string.h class Date { int mo,da,yr; char *month; public: Date(int m=0, int d=0, int y=0); ~Date(); void display() const; }; Date::Date(int m,int d,int y) { static char *mos[] = { January,February,March,April,May,June, July,August,September,October,November,December }; mo=m; da=d; yr=y; if(m!=0) { month=new char[strlen(mos[m-1])+1]; strcpy(month, mos[m-1]); } else month = 0; } Date::~Date() { delete [] month; } void Date::display() const { if(month!=0) cout<函数,首先用new运算符为字符串month动态分配了内存,然后从内部数组把月份的名字拷贝给字符串指针month。 析构函数在删除month指针时,可能会出现一些问题。当然从这个程序本身来看,没什么麻烦;但是从设计一个的角度来看,当Date用于赋值时,就会出现问题。假设上面的main()修改为“ int main() { Date birthday(8,11,1979); Date today; today=birthday; birthday.display(); return 0; } 这会生成一个名为today的空的Date型变量,并且把birthday值赋给它。如果不特别通知编译器,它会简单的认为的赋值就是成员对成员的拷贝。在上面的程序,变量birthday有一个字符型指针month,并且在构造函数里用new运算符初始化过了。当birthday离开其作用域时,析构函数调用delete运算符来释放内存。但同时,当today离开它的作用域时,析构函数同样会对它进行释放操作,而today里的month指针是birthday里的month指针的一个拷贝。析构函数对同一指针进行了两次删除操作,这会带来不可预知的后果。 如果假设today是一个外部变量,而birthday是一个自变量。当birthday离开其作用域时,就已经把对象today里的month指针删除了。显然这也是不正确的。 再假设有两个初始化的Date变量,把其一个的值赋值给另一个: Date birthday(8,11,1979); Date today(12,29,2003); today=birthday; 问题就更复杂了,当这两个变量离开作用域时,birthday的month的值已经通过赋值传递给了today。而today构造函数用new运算符给month的值却因为赋值被覆盖了。这样,birthday的month被删除了两次,而todaymonth却没有被删除掉。 二、重载赋值运算符 为了解决上面的问题,我们应该写一个特殊的赋值运算符函数来处理这问题。当需要为同一个的两个对象相互赋值时,就可以重载运算符函数。这个方法可以解决的赋值和指针的释放。 下面的程序的赋值函数用new运算符从堆分配了一个不同的指针,该指针获取赋值对象相应的值,然后拷贝给接受赋值的对象。 在重载赋值运算符的格式如下: void operator = (const Date&) 后面我们回加以改进。目前,重载的运算符函数的返回型为void。它是总的成员函数,在本程序红,是Date的成员函数。它的函数名始终是operator =,参数也始终是同一个的对象的引用。参数表示的是源对象,即赋值数据的提供者。重载函数的运算符作为目标对象的成员函数来使用。 #include iostream.h #include string.h class Date { int mo,da,yr; char *month; public: Date(int m=0, int d=0, int y=0); ~Date(); void operator=(const Date&); void display() const; }; Date::Date(int m, int d, int y) { static char *mos[] = { January,February,March,April,May,June, July,August,September,October,November,December }; mo = m; da = d; yr = y; if (m != 0) { month = new char[strlen(mos[m-1])+1]; strcpy(month, mos[m-1]); } else month = 0; } Date::~Date() { delete [] month; } void Date::display() const { if (month!=0) cout<类加入了一个重载运算符函数,这个程序和上面的一个程序是相同的。赋值运算符函数首先取得所需的数据,然后用delete把原来的month指针所占用的内存返还给堆。接着,如果源对象的month指针已经初始化过,就用new运算符为对象重新分配内存,并把源对象的month字符串拷贝给接受方。 重载的Date赋值运算符函数的第一个语句比较了源对象的地址和this指针。这个操作取保对象不会自己给自己赋值。 三、this指针 this指针是一个特殊的指针,当的某个非静态的成员函数在执行时,就会存在this指针。它指向的一个对象,且这个对象的某个成员函数正在被调用。 this指针的名字始终是this,而且总是作为隐含参数传递给每一个被声明的成员函数,例如: void Date::myFunc(Date* this); 实际编程时函数的声明不需要包含这个参数。 当程序调用某个对象的成员函数时,编译器会把该对象的地址加入到参数列表,感觉上就好象函数采用了上面所示的声明,并且是用如下方式来调用的: dt.myFunc(& dt); 静态成员函数不存在this指针。 当调用某个对象的成员函数时,编译器把对象的地址传递给this指针,然后再调用函数。因此,成员函数你对任何成员的调用实际上都隐式地使用了this指针。 1.以this指针作为返回值 使用this指针可以允许成员函数返回调用对象给调用者。前面的程序重载赋值运算符没有返回值,因此不能用如下的形式对字符串进行赋值: a=b=c; 为了使重载的赋值机制也能这样方便,必须让赋值函数返回赋值的结果,在这里就是目标对象。当赋值函数执行时,其返回值也恰好是this指针所指的内容。 下面的程序对前面那个程序进行了修改,让重载赋值运算符返回了一个Date对象的引用。 #include iostream.h #include string.h class Date { int mo,da,yr; char *month; public: Date(int m=0, int d=0, int y=0); ~Date(); void operator=(const Date&); void display() const; }; Date::Date(int m, int d, int y) { static char *mos[] = { January,February,March,April,May,June, July,August,September,October,November,December }; mo = m; da = d; yr = y; if (m != 0) { month = new char[strlen(mos[m-1])+1]; strcpy(month, mos[m-1]); } else month = 0; } Date::~Date() { delete [] month; } void Date::display() const { if (month!=0) cout<中使用this指针 在应用程序,如果数据结构里有指向自身型的成员,那么使用this指针会提供更多的方便。下面的程序建立了一个ListEntry的链表。 #include iostream.h #include string.h class ListEntry { char* listvalue; ListEntry* preventry; public: ListEntry(char*); ~ListEntry() { delete [] listvalue; } ListEntry* PrevEntry() const { return preventry; }; void display() const { cout< name; if (strncmp(name, end, 3) == 0) break; ListEntry* list = new ListEntry(name); if (prev != 0) prev->AddEntry(*list); prev = list; } while (prev != 0) { prev->display(); ListEntry* hold = prev; prev = prev->PrevEntry(); delete hold; } return 0; } 程序运行时,会提示输入一串姓名,当输入完毕后,键入end,然后程序会逆序显示刚才输入的所有姓名。 程序ListEntry含有一个字符串和一个指向前一个表项的指针。构造函数从对获取内存分配给字符串,并把字符串的内容拷贝到内存,然后置链接指针为NULL。析构函数将释放字符串所占用的内存。 成员函数PrevEntry()返回指向链表前一个表项的指针。另一个成员函数显示当前的表项内容。 成员函数AddEntry(),它把this指针拷贝给参数的preventry指针,即把当前表项的地址赋值给下一个表项的链接指针,从而构造了一个链表。它并没有改变调用它的listEntry对象的内容,只是把该对象的地址赋给函数的参数所引用的那个ListEntry对象的preventry指针,尽管该函数不会修改对象的数据,但它并不是常量型。这是因为,它拷贝对象的地址this指针的内容给一个非长常量对象,而编译器回认为这个非常量对象就有可能通过拷贝得到的地址去修改当前对象的数据,因此AddEntry()函数在声明时不需要用const。 主体:(六)对象数组和静态成员 一、对象数组 的对象和C++其他数据型一样,也可以为其建立数组,数组的表示方法和结构一样。 #include iostream.h class Date { int mo,da,yr; public: Date(int m=0,int d=0, int y=0) { mo=m; da=d; yr=y;} void display() const { cout<类对象数组和默认构造函数 在前面已经说过,不带参数或者所有参数都有默认值的构造函数叫做默认构造函数。如果没有构造函数,编译器会自动提供一个什么都不做的公共默认构造函数 。如果至少有一个构造函数,编译器就不会提供默认构造函数。 如果不含默认构造函数,则无法实例化其对象数组。因为实例花对象数组的格式不允许用初始化值来匹配某个构造函数的参数表。 上面的程序,main()函数声明了一个长度为2的Date对象数组,还有一个包含初始化值的单个Date对象。接着把这个初始化的Date对象赋值给数组第一个对象,然后显示两个数组元素包含的日期。从输出可以看到,第一个日期是有效日期,而第二个显示的都是0。 当声明了某个的对象数组时,编译器会为每个元素都调用默认构造函数。 下面的程序去掉了构造函数的默认参数值,并且增加了一个默认构造函数。 #include class Date { int mo, da, yr; public: Date(); Date(int m,int d,int y) { mo=m; da=d; yr=y;} void display() const { cout <中可以看出,Date()这个默认构造函数调用了两次。 2.对象数组和析构函数对象离开作用域时,编译器会为每个对象数组元素调用析构函数。 #include iostream.h class Date { int mo,da,yr; public: Date(int m=0,int d=0,int y=0) { mo=m; da=d; yr=y;} ~Date() {cout<函数被调用了三次,也就是dates[0],dates[1],today这三个对象离开作用域时调用的。 二、静态成员 可以把的成员声明为静态的。静态成员只能存在唯一的实例。所有的成员函数都可以访问这个静态成员。即使没有声明的任何实例,静态成员也已经是存在的。不过声明静态成员时并不能自动定义这个变量,必须在定义之外来定义该成员。 1.静态数据成员 静态数据成员相当于一个全局变量,的所有实例都可以使用它。成员函数能访问并且修改这个值。如果这个静态成员是公有的,那么的作用域之内的所有代码(不论是在的内部还是外部)都可以访问这个成员。下面的程序通过静态数据成员来记录链表首项和末项的地址。 #include iostream.h #include string.h class ListEntry { public: static ListEntry* firstentry; private: static ListEntry* lastentry; char* listvalue; ListEntry* nextentry; public: ListEntry(char*); ~ListEntry() { delete [] listvalue;} ListEntry* NextEntry() const { return nextentry; }; void display() const { cout<name
包含定义代码和测试代码,代码不复杂,有注释,池对象存取时间复杂度为常数级,多线程测试速度可达千万次/秒。 使用方法如下: 1.定义池,例如: FastPool pool; 2.往池添加对象,例如: pool.Add("abc");//要注意这里传入的是构造对象的参数 3.取出一个对象以使用,例如: std::string* p = pool.Pop(); 这一步可以跟上一步结合起来: std::string* p = pool.AddAndPop("123");//这时候p就指向"123"这个刚加入池的std::string对象 4.存回对象以重用,例如: pool.Release(p); 5.还有的时候需要删除池的对象,例如: pool.Delete(p);//只删除一个对象 pool.DeleteNotUsed();//删除池未使用的对象 pool.DeleteAll();//删除池所有对象 上面的示例只是模板参数为std::string 时的用法。还有一些函数未列出。希望代码能给大家带来启发。 重新检查了一次,发现模板代码区分和基本数据型的代码用到了另外自定义的库。需要做如下修改: #include //先定义一个模板用以区分和基本数据型的行为 templatestruct ChangeClass {typedef typename TT Type;}; templatestruct ChangeClass {typedef typename std::vector Type;}; 然后代码的 typedef typename LK::Templates::UseT::value, std::vector, T>::type TP; if (LK::Templates::IsClassOrUnion::value) 改为: typedef typename ChangeClass::value>::Type TP;//防止调用析构函数导致的错误 if (std::is_class::value) 最后,欢迎喜欢编程的伙伴来qq群244953928探讨O(∩_∩)O

16,467

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧