精华内容
下载资源
问答
  • 成员函数在调用另一个成员函数的时候,this指针变成了NULL,想请问一下是什么原因,该怎么解决呢? (数组溢出已经检查过了,都没有)
  • 空指针可以调用成员函数

    千次阅读 2016-12-24 10:16:59
    有下面一个简单类:class A { public: void fun(){ ...讲道理,空指针应当是”不可用”的,自然也不能调用成员函数了,但是结果却出乎意料地正确执行了: 为什么??其实,关键在于每个成员函数的this

    有下面一个简单类:

    class A
    {
    public:
        void fun(){
            cout << "I'm class A"<<endl;
        }
    };

    用一个空指针调用上面的fun函数:

    A* pa = NULL;
    pa->fun();

    讲道理,空指针应当是”不可用”的,自然也不能调用其成员函数了,但是结果却出乎意料地正确执行了:
    这里写图片描述

    为什么??其实,关键在于每个成员函数的this指针。
    其实,c++类的成员函数与传统c的函数并没有什么不同,只是成员函数的第1个参数必须是this指针,只是这个参数是由编译器自动加上去的,调用时也不需要显示传递。

    所以,上面的class A的成员函数fun的本质其实是这样子的:

    void fun(A* this){
            cout << "I'm class A"<<endl;
        }

    而调用是其实这样子的:

    A* pa = NULL;
    fun(pa);

    可以看到,虽然this指针实参为NULL,但是fun函数根本没有用到this指针啊,this在这里只是打了个酱油而已!

    但是,如果在成员函数中使用的成员变量,就不可避免要使用到this指针,若是这样,就不可避免的出现崩溃了。

    综上所述:即便对象指针为NULL,只要成员函数没有使用非静态成员函数,就可以正常调用该成员函数。

    展开全文
  • 比如类内绑定自身成员函数作为回调函数,这种情况开启多线程时很常见,如果不想将回调定义为全局那只能定义为类静态了,为了避免过度破坏封装类中应当尽量不要让类静态成原函数调用类成员。这种情况下可以用一种...

     

    在类中使用静态成员函数是一种破坏封装的行为,因为静态成员函数只能调用类的静态成员。但是在有些情况下只能使用静态成员函数,比如类内绑定自身成员函数作为回调函数,这种情况在开启多线程时很常见,如果不想将回调定义为全局那只能定义为类静态了,为了避免过度破坏封装类中应当尽量不要让类静态成原函数调用类成员。这种情况下可以用一种比较取巧的方法。

    因为类的静态成员和普通成员其实就一种区别,那就是静态成员本身没有this指针,所以静态成员属于类而不属于类对象。如果我们想在类的静态成员函数里面调用类的普通成员,只需要把类指针当做参数传入静态成员函数里面,静态成员函数可以使用这个指针调用类的普通成员,demo如下:

    #include "stdafx.h"
    #include <string>
    #include <iostream>
    using namespace std;
    class A
    {
    
    private:
    	string str;
    public:
    	
    	void handler(string _str)
    	{
    		str = _str;
    		cout<<str<<endl;
    		
    	}
    	static void handler_static(void *p_A,string _str)
    	{
    	   A *_p =  static_cast<A*>(p_A);
    	   _p->handler(_str);
    	}
    	
    	void start()
    	{
    		void *p = this;
    		string _str = "static";
    		handler_static(p,_str);
    	}
    
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	A a;
    	a.start();	
    	getchar();
    	return 0;
    }
    

     handler_static为静态成员函数,入参有两个,第一个为本身对象this指针,另一个为实际需要传入的参数。在这里实现的功能为使用类的静态成员函数给类的普通成员变量str赋值,如果不采用这种传入this指针间接调用的方式,那str也必须定义为静态的。当然在这里看起来没多大意义,只是用一个handler成员函数接收this指针间接实现了功能。但是在代码复杂的情况下这种方式可以避免过度破坏封装,在必须使用静态的地方(比如绑定回调)才定义静态成员,就不会有静态成员使用的连锁反应了。

    展开全文
  • 回调函数调用成员函数的方法

    千次阅读 2012-10-26 15:55:48
    利用MFC或者其它的C++应用编写回调函数是非常麻烦的,其根本原因是回调函数是基于C编程的Windows SDK的技术,不是针对C++的,程序员可以将一个C函数直接作为回调函数,但是如果试图直接使用C++的成员函数作为回调...

            利用MFC或者其它的C++应用编写回调函数是非常麻烦的,其根本原因是回调函数是基于C编程的Windows SDK的技术,不是针对C++的,程序员可以将一个C函数直接作为回调函数,但是如果试图直接使用C++的成员函数作为回调函数将发生错误,甚至编译就不能通过。其错误是普通的C++成员函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递一个指向自身的指针给其成员函数从而实现程序函数可以访问C++的数据成员。这也可以理解为什么C++类的多个实例可以共享成员函数但是确有不同的数据成员。由于this指针的作用,使得将一个CALLBACK型的成员函数作为回调函数安装时,就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数安装失败。

            要解决这一问题的关键就是不让this指针起作用,有以下两种方法:

                1、不使用成员函数,直接使用普通C函数,而为了实现在C函数中可以访问类的成员变量,可以使用 friend 操作符,在C++中将该C函数说明为类的友元即可。

                2、使用静态成员函数,静态成员函数不使用this指针作为隐含参数,这样就可以作为回调函数了。

            静态成员函数具有两大特点:

                其一,可以在没有类实例的情况下使用;

                其二,只能访问静态成员变量和静态成员函数,不能访问非静态成员变量和非静态成员函数。

            由于在C++中使用类成员函数作为回调函数的目的就是为了访问所有的成员变量和成员函数,如果作不到这一点将不具有实际意义。解决的办法也很简单,就是使用一个静态类指针作为类成员,通过在类创建时初始化该静态指针,如m_pThis=this,然后在回调函数中通过该静态指针就可以访问所有成员变量和成员函数了。这种处理办法适用于只有一个类实例的情况,因为多个类实例将共享静态类成员和静态成员函数,这就导致静态指针指向最后创建的类实例。为了避免这种情况,可以使用回调函数的一个参数来传递this指针,从而实现数据成员共享。这种方法稍稍麻烦,这里就不再赘述。


    展开全文
  • 回调函数中调用类中的非静态成员变量或非静态成员函数 【问题1】如何类中封装回调函数? 【答】:  a.回调函数只能是全局的或是静态的。  b.全局函数会破坏类的封装性,故不予采用。  c.静态函数只能访问...
    回调函数中调用类中的非静态成员变量或非静态成员函数

    【问题1】如何在类中封装回调函数?

    【答】:
      a.回调函数只能是全局的或是静态的。
      b.全局函数会破坏类的封装性,故不予采用。
      c.静态函数只能访问类的静态成员,不能访问类中非静态成员。
     
     【问题2】如何让静态函数访问类的非静态成员?
        【解决方案】:

        声明一静态函数a(),将类实例对象指针做为参数传入。如:
      class A()
      {
          static void a(A *); //静态函数
          void b();  //非静态函数 
      }  
      void A::a(A * pThis)
      {
       pThis->b(); //静态函数中调用非静态函数 
      }
         
     【问题3】回调函数中如何访问非静态成员?
      由于回调函数往往有固定定义,并不接受  A * pThis 参数
      如:CALLBACK MyTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime);
      
            【解决方案1】:本方案当遇到有多个类实例对象时会有问题。原因是pThis指针只能指向一个对象。
      class A()
      {
          static void a(); //静态回调函数
          void b();  //非静态函数 
          static A * pThis;   //静态对象指针
      }  
      
      A * A::pThis=NULL;
      A::A()   //构造函数中将this指针赋给pThis,使得回调函数能通过pThis指针访问本对象
      {
           pThis=this;
      }
      void A::a()
      {
          if (pThis==NULL) return;
          pThis->b(); //回调函数中调用非静态函数 
      }
      
      【解决方案2】:本方案解决多个类实例对象时方案1的问题。用映射表存所有对象地址,每个对象保存自己的ID号。
      typedef CMap<UINT,UINT,A*,A*> CAMap;
      class A()
      {
          static void a(); //静态回调函数
          void b();  //非静态函数 
          int m_ID;  //本对象在列表中的ID号
          static int m_SID;   //静态当前对象ID        (需要时,将m_ID赋值给m_SID以起到调用本对象函数的功能)
          static CAMap m_Map; //静态对象映射表
      }  
      
      CAMap A::m_Map;
      int   A::m_SID=0;
      
     
      A::A()   //构造函数中将this指针赋给pThis,使得回调函数能通过pThis指针访问本对象
      {
          if(m_Map.IsEmpty())
          {
       m_ID=1;
          }
          else
          { 
            m_ID=m_Map.GetCount()+1;
          }
          m_Map.SetAt( m_ID, this );
      }
      void A::a()
      {
          if (m_Map.IsEmpty()) return;
          A * pThis=NULL;
          if(m_Map.Lookup(m_SID,pThis))
          {
       pThis->b(); //回调函数中调用非静态函数 
          };
      }

    =================================

     

    C++中类成员函数作为回调函数


    回调函数是基于C编程的Windows SDK的技术,不是针对C++的,程序员可以将一个C函数直接作为回调函数,但是如果试图直接使用C++的成员函数作为回调函数将发生错误,甚至编译就不能通过。 

    普通的C++成员函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递一个指向自身的指针给其成员函数从而实现程序函数可以访问C++的数据成员。这也可以理解为什么C++类的多个实例可以共享成员函数但是确有不同的数据成员。由于this指针的作用,使得将一个CALLBACK型的成员函数作为回调函数安装时就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数安装失败。

    这样从理论上讲,C++类的成员函数是不能当作回调函数的。但我们在用C++编程时总希望在类内实现其功能,即要保持封装性,如果把回调函数写作普通函数有诸多不便。经过网上搜索和自己研究,发现了几种巧妙的方法,可以使得类成员函数当作回调函数使用。

    这里采用Linux C++中线程创建函数pthread_create举例,其原型如下:

    1. int pthread_create( pthread_t *restrict tidp , const pthread_attr_t *restrict attr , void* (*start_rtn)(void*) , void *restrict arg );  

    第一个参数为指向线程标识符的指针。
    第二个参数用来设置线程属性。
    第三个参数是线程运行函数的起始地址,即回调函数。
    最后一个参数是运行函数的参数。

    这里我们只关注第三个参数start_run,它是一个函数指针,指向一个以void*为参数,返回值为void*的函数,这个函数被当作线程的回调函数使用,线程启动后便会执行该函数的代码。

    方法一:回调函数为普通函数,但在函数体内执行成员函数
    见以下代码:
    1. class MyClass  
    2. {  
    3.     pthread_t TID;  
    4. public:  
    5.     void func()  
    6.     {  
    7.         //子线程执行代码  
    8.     }  
    9.   
    10.     bool startThread()  
    11.     {//启动子线程  
    12.         int ret = pthread_create( &TID , NULL , callback , this );  
    13.         if( ret != 0 )  
    14.             return false;  
    15.         else  
    16.             return true;  
    17.     }  
    18. };  
    19.   
    20. static void* callback( void* arg )  
    21. {//回调函数  
    22.     ((MyClass*)arg)->func();调用成员函数  
    23.     return NULL;  
    24. }  
    25.   
    26. int main()  
    27. {  
    28.     MyClass a;  
    29.     a.startThread();  
    30. }  

    类MyClass需要在自己内部开辟一个子线程来执行成员函数func()中的代码,子线程通过调用startThread()成员函数来启动。这里将回调函数callback写在了类外面,传递的参数是一个指向MyClass对象的指针(在pthrad_create()中由第4个参数this指定),回调函数经过强制转换把void*变为MyClass*,然后再调用arg->func()执行子线程的代码。

    这样做的原理是把当前对象的指针当作参数先交给一个外部函数,再由外部函数调用类成员函数,以外部函数作为回调函数,但执行的是成员函数的功能,这样相当于在中间作了一层转换。缺点是回调函数在类外,影响了封装性,这里把callback()限定为static,防止在其它文件中调用此函数。


    方法二:回调函数为类内静态成员函数,在其内部调用成员函数

    在方法一上稍作更改,把回调函数搬到类MyClass里,这样就保持了封装性。代码如下:

    1. class MyClass  
    2. {  
    3.     static MyClass* CurMy;//存储回调函数调用的对象  
    4.     static void* callback(void*);//回调函数  
    5.     pthread_t TID;  
    6.     void func()  
    7.     {  
    8.         //子线程执行代码  
    9.     }  
    10.       
    11.     void setCurMy()  
    12.     {//设置当前对象为回调函数调用的对象  
    13.         CurMy = this;  
    14.     }  
    15. public:  
    16.     bool startThread()  
    17.     {//启动子线程  
    18.         setCurMy();  
    19.         int ret = pthread_create( &TID , NULL , MyClass::callback , NULL );  
    20.         if( ret != 0 )  
    21.             return false;  
    22.         else  
    23.             return true;  
    24.     }  
    25. };  
    26. MyClass* MyClass::CurMy = NULL;  
    27. void* MyClass::callback(void*)  
    28. {  
    29.     CurMy->func();  
    30.     return NULL;  
    31. }  
    32.   
    33. int main()  
    34. {  
    35.     MyClass a;  
    36.     a.startThread();  
    37. }  
    类MyClass有了1个静态数据成员CurMy和1个静态成员函数callback。CurMy用来存储一个对象的指针,充当方法一中回调函数的参数arg。callback当作回调函数,执行CurMy->func()的代码。每次建立线程前先要调用setCurMy()来让CurMy指向当前自己。

    这个方法的好处时封装性得到了很好的保护,MyClass对外只公开一个接口startThread(),子线程代码和回调函数都被设为私有,外界不可见。另外没有占用callback的参数,可以从外界传递参数进来。但每个对象启动子线程前一定要注意先调用setCurMy()让CurMy正确的指向自身,否则将为其它对象开启线程,这样很引发很严重的后果。


    方法三:对成员函数进行强制转换,当作回调函数

    代码如下:

    1. class MyClass  
    2. {  
    3.     pthread_t TID;  
    4.     void func()  
    5.     {  
    6.         //子线程执行代码  
    7.     }  
    8. public:  
    9.     bool startThread()  
    10.     {//启动子线程  
    11.         typedef void* (*FUNC)(void*);//定义FUNC类型是一个指向函数的指针,该函数参数为void*,返回值为void*  
    12.         FUNC callback = (FUNC)&MyClass::func;//强制转换func()的类型  
    13.         int ret = pthread_create( &TID , NULL , callback , this );  
    14.         if( ret != 0 )  
    15.             return false;  
    16.         else  
    17.             return true;  
    18.     }  
    19. };  
    20.   
    21. int main()  
    22. {  
    23.     MyClass a;  
    24.     a.startThread();  
    25. }  
    这个方法是原理是, MyClass::func最终会转化成 void func(MyClass *this); 也就是说在原第一个参数前插入指向对象本身的this指针。 可以利用这个特性写一个非静态类成员方法来直接作为线程回调函数。对编译器而言,void (MyClass::*FUNC1)()和void* (*FUNC)(void*)这两种函数指针虽然看上去很不一样,但他们的最终形式是相同的,因此就可以把成员函数指针强制转换成普通函数的指针来当作回调函数。在建立线程时要把当前对象的指针this当作参数传给回调函数(成员函数func),这样才能知道线程是针对哪个对象建立的。

    方法三的封装性比方法二更好,因为不涉及多个对象共用一个静态成员的问题,每个对象可以独立地启动自己的线程而不影响其它对象。



    展开全文
  • C++箴言:避免析构函数调用函数

    千次阅读 2007-01-10 10:58:00
    原文地址:http://blog.csdn.net/pdiy/archive/2005/12/14/551983.aspx 如果你已经从另外一种语言如C#或者Java转向了C++,你会觉得,避免在类的构造函数或者析构函数调用函数这一原则有点违背直觉。但是C++中...
  • 读Effective C++ 条歀09:绝不构造和析构过程中调用函数(Never call virtual functions during construction or destruction) 首先,我们用一句程序员中比较流行的话作为本文的开篇:如果你基类的构造...
  • 可以把共用的代码封装成一个私有的成员函数,然后构造函数内统一调用。 1.2子类构造函数调用基类构造函数 —–基类有默认构造函数时,可以子类不写,则隐式调用 —–基类无/有默认构造函数时,子类构造函数...
  • 代码描述:实现客户机(CLIENT)类。定义字符型静态数据成员ServerName,...定义静态函数ChangeServerName()改变服务器名称。 #include using namespace std; class Client { public: Client(); ~Client(); voi
  • 构造函数调用函数函数的入口地址是编译时静态确定的,并未实现虚调用。但是为什么构造函数调用函数,实际上没有发生动态联编呢?1. 不要构造函数调用函数的原因第一个原因,概念上,构造...
  • c中,为了解决一些频繁调用的小函数大量消耗栈空间的问题,特别的引入了inline修饰符,表示为内联函数。 栈空间就是指放置程式的局部数据也就是函数内数据的内存空间,系统下,栈空间是有限的,假如频繁大量的...
  • 可以,但是达不到想要的效果,应该尽可能避免在构造函数和析构函数调用函数。 class base{ public: base(){ cout(); } private: virtual size_t size(){ return sizeof(*this); } }; class
  • 这里先运行个示例代码: #include<iostream> using ...因此,一般情况下,应该避免在构造函数和析构函数调用函数,如果一定要这样做,程序猿必须清楚,这是对虚函数调用其实是实调用
  • 基类中定了纯虚拟函数...基类某个函数调用该纯虚函数,本意是为了使用多态, 1. 基类的构造函数调用函数,此时派生类派生类还未构造成功; 2.或是基类的析构函数调用函数,此时派生类以被销毁;
  • 构造函数调用函数

    千次阅读 2016-03-19 19:22:54
    问题在于构造函数调用函数无法实现多态。 看下面的代码: class A { public: A() { show(); }//行4 virtual void show() { cout ; } void anotherShow(){show();}//行6 }; class B:public A { public
  • 避免在析构函数中编写代码

    千次阅读 2014-07-21 11:55:57
    今天这篇我们讨论下为什么应该避免在析构函数中编写代码。即让析构函数为空。 例如: virtual ~MyClass() { } 我们用空析构函数这个术语表示花括号内没有代码的析构函数。 需要编写析构函数可能有如下几个原因: ...
  • VB调用API函数

    万次阅读 多人点赞 2016-01-13 20:17:36
    API函数快速入门--怎样VB中声明和使用API函数--     一、VB中声明API函数有两种方法:如果我们只某个窗体中使用API函数,我们可以窗体代码的 General部分声明它:    声明的语法是:  Private ...
  • 我想以重复本文的主题开篇:不要类的构造或者析构函数调用函数,因为这种调用不会如你所愿,即使成功一点,最后还会使你沮丧不已。如果你以前是一个Java或者C#程序员,请密切注意本节的内容-这正是C++与其它...
  • C中如何调用C++函数、类内函数

    千次阅读 2012-09-14 20:00:02
    C中如何调用C++函数的问题,简单回答是将函数用extern "C"声明,当被问及如何将类内成员函数声明时,一时语塞,后来网上查了下,网上有一翻译C++之父的文章可以作为解答,遂拿来Mark一下。 将C++函数声明为...
  • 构造或析构期间不要调用 virtual函数,因为这样的调用从不下降至派生类(比起当前执行构造函数和析构函数的那层)。  不应该构造或析构期间调用 virtual函数,因为这样的调用不会如你想象那样工作,而且会让...
  • 静态成员函数

    千次阅读 2007-08-21 09:26:00
    静态成员函数有以下特点: 可以通过class::function方式调用,不用生成实例 不能访问非静态的成员变量 静态成员函数是类的一部分,而非对象的一部分。 非静态成员函数调用时还隐式的传进一个this指针,静态成员函数...
  • const修饰成员函数总结

    千次阅读 2012-04-26 22:46:39
    const修饰成员函数有两个理由 1 让类的接口更容易理解,因为这可以方便程序员知道那个函数可以改变对象内容,而那个函数不能改。 2 让类能够操作const对象。 有一个非常重要的C++特性是 两个成员函数,如果只是...
  • 子类对象调用函数和赋值时,为了区分调用函数和变量,通常使用域名作用符区分,如果不加作用域符,默认情况下谁的类声明的对象调用各自的函数和变量 #include &amp;lt;iostream&amp;gt; using ...
  • 内联函数与普通函数的区别 ...内联函数可以避免这样的消耗,一个函数申明为内联函数编译的过程中,编译器会将函数体代码插入函数调用处,这样虽然会增加代码段的空间,但是可以避免时间上的消耗,提高了
  • C语言函数调用栈(一)

    万次阅读 多人点赞 2018-07-19 22:16:25
    程序的执行过程可看作连续的函数调用。当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行。函数调用过程通常使用堆栈实现,每个用户态进程对应一个调用栈结构(call stack)。编译器使用...
  • 转自:http://blog.chinaunix.net/uid-10673338-id-2936852.html 转自:...   对以上两篇文章,我添加了自己已有的部分知识,并重新地汇总整理 ... ...从函数定义的位置来粗略理解
  • 构造期间,首先构造基类部分,此时继承类成员变量尚未初始化,如果允许调用子类虚函数,而子类成员函数很有可能要调用子类成员变量,为了避免使用未初始化的成员变量,C++拒绝构造函数中调用子类重载的虚函数。...
  • 成员函数2. 构造函数2.1 如何定义2.2 如何使用2.3 还有一种写法2.4 示例:创建链表节点3. 与类的区别 1. 成员函数 示例,利用成员函数初始化成员数据: struct Students { // 这是普通成员数据 string name; int ...
  • C++利用对象、引用、指针调用函数函数实现原理说明: 每个类的大小比起所有成员数据多4个字节,表明有虚函数的类的大小还要加上一个紧缩的空指针类型的大小。这说明了该包含虚函数的类中,编译系统自动加入了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 231,336
精华内容 92,534
关键字:

为了避免在调用成员函数