c++静态函数调用非静态成员
2013-05-20 13:46:20 leileicaocao 阅读数 504

c++支持三种类型的成员函数,分别为static,nostatic,virtual。每一种调用方式都不尽相同。

非静态成员函数(Nonstatic Member Functions)

保证nostatic member function至少必须和一般的nonmember function有相同的效率是C++的设计准则之一。事实上在c++中非静态成员函数(nostatic member function)与普通函数的调用也确实具有相同的效率,因为本质上非静态成员函数就如同一个普通函数,如一个非静态成员函数Xfloat Point::X();就相当于一个普通函数float X(Point* this);。编译器内部会将成员函数等价转换为非成员函数,具体是这样做的:

1.改写成员函数的签名,使得其可以接受一个额外参数,这个额外参数即是this指针:

float Point::X();
//成员函数X被插入额外参数this
float Point:: X(Point* this );

当然如果成员函数是const的,插入的参数类型将为 const Point* 类型。

2.将每一个对非静态数据成员的操作都改写为经过this操作。

3.将成员函数写成一个外部函数,对函数名进行“mangling”处理,使之成为独一无二的名称。

可以看出,将一个成员函数改写成一个外部函数的关键在于两点,一是给函数提供一个可以直接读写成员数据的通道; 二是解决好有可能带来的名字冲突。第一点通过给函数提供一个额外的指针参数来解决,第二点则是通过一定的规则将名字转换,使之独一无二。

由此可以做出一点总结:一个成员函数实际上就是一个被插入了一个接受其类的指针类型的额外参数的非成员函数, 当然还要额外对函数的名称进行处理。额外插入的参数用来访问数据成员,而名称的特殊处理用来避免名字冲突。

对于名称的特殊处理并没有统一的标准,各大编译器厂商可能有不同的处理规则。 在VC下上述的成员函数X()的名称X处理后就成了?X@Point@@QAEMXZ 更多信息可以参见维基百科的Visual C++名字修饰

于是在VC中对于上面的例子中的成员函数的调用将发生如下的转换:

//p->X();被转化为
?X@Point@@QAEMXZ(p);
//obj.X();被转化为
?X@Point@@QAEMXZ(&obj);

虚拟成员函数(Virtual Member Functions)

如果function()是一个虚拟函数,那么用指针或引用进行的调用将发生一点特别的转换——一个中间层被引入进来。例如:

// p->function()
//将转化为
(*p->vptr[1])(p);
  • 其中vptr为指向虚函数表的指针,它由编译器产生。vptr也要进行名字处理,因为一个继承体系可能有多个vptr。
  • 1是虚函数在虚函数表中的索引,通过它关联到虚函数function().

何时发生这种转换?答案是在必需的时候——一个再熟悉不过的答案。当通过指针调用的时候,要调用的函数实体无法在编译期决定,必需待到执行期才能获得,所以上面引入一个间接层的转换必不可少。但是当我们通过对象(不是引用,也不是指针)来调用的时候, 进行上面的转换就显得多余了,因为在编译器要调用的函数实体已经被决定。此时调用发生的转换,与一个非静态成员函数(Nonstatic Member Functions)调用发生的转换一致。

静态成员函数(Static Member Functions)

静态成员函数的一些特性:

  1. 不能够直接存取其类中的非静态成员(nostatic members),包括不能调用非静态 成员函数(Nonstatic Member Functions)。
  2. 不能够声明为 const、voliatile或virtual。
  3. 它不需经由对象调用,当然,通过对象调用也被允许。

除了缺乏一个this指针他与非静态成员函数没有太大的差别。在这里通过对象调用和通过指针或引用调用,将被转化为同样的调用代码。

需要注意的是通过一个表达式或函数对静态成员函数进行调用,被C++ Standard要求对表达式进行求值。如:

(a+=b).static_fuc();

虽然省去对a+b求值对于static_fuc()的调用并没有影响,但是程序员肯定会认为表达式a+=b已经执行,一旦编译器为了效率省去了这一步,很难说会浪费多少程序员多少时间。这无疑是一个明智的规定。

2017-04-11 20:40:50 xiaodeROSE 阅读数 2366

近学习到C++ primer中关于类中静态变量的部分,有一道课后题非常有意思。

题目是关于静态变量在类中的初始化。

class example{
public:
    //static double rate=6.5;
    static const int size;
     std::vector<double> vec(size);
};

代码是这样的。

当时我一看,觉得没有错误啊。

于是编译的时候总是出错,如下图。










这个错误提示说,这个类型是未定义的。

size这个变量不是const int类型的吗,没有问题啊。

在网上看了好几篇文章之后懂了。

在C++中,类对象的构造顺序是这样的:

1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员
2.进入构造函数后在构造函数中执行一般计算

也就是说,在C++中,类中的成员在定义的时候不能被初始化,只有经过构造函数的构造之后才能被初始化。

所以在这里,我们只能定义类中的静态容器vector


像这样:

static std::vector<double> vec;
这样子才是正确的写法。

C++中对于类中的静态变量的初始化方式是先在类中定义,再在类外面初始化。
如果想在类中初始化这个vector容器的话,可以写一个构造函数,比如:

 class example{
 public:
 vector<double> vec;
 example(): vec(5){}
 };


这样子就可以解决了。最后总结一下关于类成员的定义和初始化:
1,类内成员在被定义时是不能被初始化的,只能通过构造函数来进行初始化。
2,类内静态变量的初始化方式是先在类内定义,再到类外面进行初始化。



2017-05-05 13:25:00 weixin_34413802 阅读数 1

C++静态成员和非静态成员的区别

1.数据成员可以分静态变量非静态变量两种.

静态成员:静态类中的成员加入static修饰符,即是静态成员.可以直接使用类名+静态成员名访问此静态成员,因为静态成员存在于内存,非静态成员需要实例化才会分配内存,所以静态成员不能访问非静态的成员..因为静态成员存在于内存,所以非静态成员可以直接访问类中静态的成员.静态成员在每个类中只有一个拷贝,是解决同一个类的不同对象之间数据和函数共享问题的。
 非成静态员:所有没有加Static的成员都是非静态成员,当类被实例化之后,可以通过实例化的类名进行访问..非静态成员的生存期决定于该类的生存期..而静态成员则不存在生存期的概念,因为静态成员始终驻留在内容中..
  一个类中也可以包含静态成员和非静态成员,类中也包括静态构造函数和非静态构造函数..

2.静态成员函数可以直接引用该类的静态数据成员和静态成员函数,但不能直接引用非静态数据成员和非静态成员函数,否则编译报错。

如果要引用,必须通过参数传递的方式得到对象名,然后再通过对象名引用

例1:

  1. class A  
  2. {  
  3.       public:  
  4.       static void f(A a);  
  5.      private:  
  6.           int x;  
  7. };  
  8. void A::f(A a)  
  9. {  
  10.       cout<<x<<endl;//对x的引用是错误的  
  11.      cout<<a.x<<endl;//正确的  
  12. }  

例2:

  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. class Myclass  
  5. {  
  6.     private:  
  7.         int     m;  // 非静态数据成员  
  8.         static  int n;  // 静态数据成员  
  9.     public:  
  10.         Myclass();  // 构造函数  
  11.         static  int getn(Myclass a);    // 静态成员函数  
  12. };  
  13.   
  14. Myclass::Myclass()  
  15. {  
  16.     m = 10;  
  17. }  
  18.   
  19. int Myclass::getn(Myclass a)  
  20. {  
  21.     cout << a.m << endl;    // 通过类间接使用  非静态数据成员  
  22.     return n;       // 直接使用  静态数据成员  
  23. }  
  24.   
  25. int Myclass::n = 100;   // 静态数据成员初始化  
  26.   
  27. void    main()  
  28. {  
  29.     Myclass app1;  
  30.     cout << app1.getn(app1) << endl;    // 利用对象引用静态函数成员  
  31.     cout << Myclass::getn(app1) << endl;    // 利用类名引用静态函数成员  
  32. }  

那么可以得出结论,静态成员和非静态成员区别如下:

1.访问方式不一样.静态成员(通过类名.静态成员名访问).非静态成员(通过对象名.非静态成员名访问)

2.静态成员属于类.该类的所有对象共同拥有这一个成员.

非静态成员属于对象,每个对象都有一份.静态成员不论有类有多少个对象.只在内存中分配一块空间.

转载于:https://my.oschina.net/lmoon/blog/893177

2013-03-05 20:06:42 xiaoleiacm 阅读数 784

 

 

1,静态成员函数调用非静态成员函数

静态成员函数可以直接引用该类的 静态数据成员 和 静态成员函数,但不能直接引用 非静态数据成员 和 非静态成员函数,否则编译报错。如果要引用,必须通过参数传递的方式得到对象名,然后再通过对象名引用

#include<iostream>
using namespace std;
class Myclass
{
private:
    int	m;	// 非静态数据成员
    static	int	n;	// 静态数据成员
public:
    Myclass();// 构造函数
    static	int	getn(Myclass a);	// 静态成员函数
};
Myclass::Myclass()
{
    m = 10;
}
int	Myclass::getn(Myclass a)
{
    cout << a.m << endl;	// 通过类间接使用  非静态数据成员
    return n;		// 直接使用  静态数据成员
}
int	Myclass::n = 100;	// 静态数据成员初始化
int	main()
{
    Myclass app1;
    cout << app1.getn(app1) << endl;	// 利用对象引用静态函数成员
    cout << Myclass::getn(app1) << endl;	// 利用类名引用静态函数成员
}


非静态成员函数后面加const(加到非成员函数或静态成员后面会产生编译错误),表示成员函数隐含传入的this指针为const指针,决定了在该成员函数中,任意修改它所在的类的成员的操作都是不允许的(因为隐含了对this指针的const引用);唯一的例外是对于mutable修饰的成员。加了const的成员函数可以被非const对象和const对象调用,但不加const的成员函数只能被非const对象调用。

 

class A
{
private:
    int m_a;
public:
    A() : m_a(0) {}
    int getA() const
    {
        return m_a; //同return this->m_a;。
    }
    int GetA()
    {
        return m_a;
    }
    int setA(int a) const
    {
        m_a = a; //这里产生编译错误,如果把前面的成员定义int m_a;改为mutable int m_a;就可以编译通过。

    }
    SetA(int a)
    {
        m_a = a; //同this->m_a = a;
    }
};
A a1;
const A a2;
int t;
t = a1.getA();
t = a1.GetA();
t = a2.getA();
t = a2.GetA(); //a2是const对象,调用非const成员函数产生编译错误。
一般对于不需修改操作的成员函数尽量声明为const成员函数


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2011-11-04 00:01:00 wwater 阅读数 9

只要学了C++的人,肯定知道静态联编和动态联编,如果你不知道,ok那你学习之路还长。简单的静态联编的东西就不说了。先看下面程序。

class AA{
public:
void result()
{
std::cout << "Surprise?" << std::endl;
};
};
int main()
{
AA *p = NULL;
p->result();
system("Pause");
return 0;
}

上面程序运行会报错吗?

——————————————————————

如果你说运行一切正常并知道原因,ok。那就别往下看了,时间就是金钱。

确实,这个运行正常并输出 Surprise? 不信?你copy过去运行下试试。为啥啊。明明指针p的值是NULL,而你使用NULL指针去调用成员函数,明明会报内存错误的瑟。书上不是说了不能使用 NULL指针吗?嘿嘿,没错,确实不能使用NULL指针,但是这里,程序根本就没有用指针p的值,而是仅仅用到了它的类型做静态束定而已。

要解此题首先要明确两个问题。

1、静态联编的原理;2、成员函数的代码在运行期只有一份拷贝。

静态联编简单的说就是在编译期就已经确定了要调用哪个函数了,这里的result()就是。同时要知道,类的成员函数在运行期只有一份拷贝在内存,不管类的实例有多少个,成员函数始终只有一份代码在内存,因此只要知道类的指针的类型之后,就可以定位到函数的入口地址,根本不关心该指针指向的是一个什么东西。成员函数和成员变量不一样,非静态成员变量是跟随类的实例走的。

ok,明白上面两个问题之后,这个事情就好解决了。直接上汇编吧。

汇编如下:

AA *p = NULL;
00411ACE mov dword ptr [p],0
p->result();
00411AD5 mov ecx,dword ptr [p]
00411AD8 call AA::result (41105Ah)

清楚了吧。在执行p->result()的时候只是把p的值移动到了一个暂存器里面,但是并没有用到这个值,后面就直接调用AA::result函数了,0x41105A正是该函数的入口地址。

ok,好了。不仅可以向以上说的去访问成员函数,甚至再过分一点((A*)0)->result();这样都可以。你再火一点把那个0换成任意一个地址都可以正确调用到那个函数,因为编译器在静态束定的时候只关心那个指针的类型。当然了,不可这样去访问类的成员变量,因为成员变量是在对象的内存布局里面的。

值得说一点的是,如果你在result函数里面有涉及到类的成员变量的访问,那么这显然就会出错了,因为成员变量需要通过传进来的this指针(其实就可以理解成时p指针)去访问对象的内存的。然而此时p还没有指向一个有效的空间。故而出错。


关于C++的成员函数调用

阅读数 531

在C++中,我们都知道类的静态成员函数是可以

博文 来自: l_z_s_001

C++ - 对象模型之 成员函数调用

阅读数 2247

C++对象模型目录C++-对象模型之编译器何时才会自行添加构造函数C++-对象模型之内存布局C++-对象模型之成员函数调用C++-对象模型之构造和析构函数都干了什么C++-对象模型之类对象在执行时是如何生成的C++-对象模型之模板、异常、RTTI的实现C++-对象模型之成员函数调用

博文 来自: Gykimo

解析c++静态联编和类的成员函数调用

阅读数 812

只要学了C++的人,肯定知道静态联编和动态联编,如果你不知道,ok那你学习之路还长。简单的静态联编的东西就不说了。先看下面程序。classAA{ public:    voidresult()      {           std::cout      }; };intmain(){   AA*p=NULL;   p->re

博文 来自: neiloid

C++静态成员和非静态成员的区别

阅读数 135

设计程序经常会有这种需求,某个类里的方法能够全局访问.在这种情况下有两种实现方案:1>单例模式(Singleton);2>静态方法.比较两种方式:1>什么时候使用静态类代替singleton:这里有几个很好的静态类比singleton更好的应用场景.最基本的例子就是在Java中的java.lang.Math类的实现方式,Math类就是用过静态方法来实现的,而不是单例

博文 来自: Programer_vc

解析c++静态联编和类的成员函数调用

阅读数 6

只要学了C++的人,肯定知道静态联编和动态联编,如果你不知道,ok那你学习之路还长。简单的静态联编的东西就不说了。先看下面程序。classAA{public:voidresult(){std::cout&lt;&lt;"Surprise?"&lt;&lt;std::endl;};};intmain(){AA*p=NULL;...

博文 来自: wwater
没有更多推荐了,返回首页