精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>编程开发>>C/C++>>C、C++语言基础>>关于指向类成员的指针

主题:关于指向类成员的指针
发信人: ago()
整理人: wenbobo(2002-12-06 22:08:55), 站内信件
class A
{
    public:
        int i;
};

int A::*pi=0;

这最后一句,的结果是给class A的定义中增加了一个属性吗?
A中没有定义int *pi,
有了最后一句,A中多了一个int指针,属性?

也不知道我问的问题写的请不清楚,
请回信.
小问题,见笑了.

--
※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.97.228.249]
发信人: crazyjava (骄傲的中国人), 信区: C
标  题: Re: 小问题-...
发信站: 网易虚拟社区 (Mon Jun 14 17:00:14 1999), 站内信件

【 在 ago (ago) 的大作中提到: 】
: class A
: {
:     public:
:         int i;
:    .......

编译能通过吗?我认为就不行。

int A::*pi=0; 只是对pi初始化而已.
一般这样写,是由于 pi 是 static member of A, 

for example:
class A 

   public: 
     int i;
     static int *pi;
}; 

int A::*pi=0; 

--
孤身走我路...
其实,路,两个人一起走比一个人要好。
email: [email protected]

※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 139.87.93.157]
发信人: ago (ago), 信区: C
标  题: Re: 小问题-...
发信站: 网易虚拟社区 (Mon Jun 14 17:17:29 1999), 站内信件

程序写法没错,出自VC++6.0教材,关于"成员指针转换"部分钟的例程,
还有如下代码:

class AClass
{
    public:
        int I1;
        Show(){cout << I1 <<"\n"}
};

int AClass::*pI1=&AClass::I1;

void main()
{
AClass aClass;
AClass *paClass=&aClass;

int i;
aClass.*pI1=887;
aClass.Show();
i=paClass->*pI1;

    cout << i << "\n";
}

--
※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.97.228.249]
发信人: crazyjava (骄傲的中国人), 信区: C
标  题: Re: 小问题-...
发信站: 网易虚拟社区 (Mon Jun 14 18:51:35 1999), 站内信件

哈哈,一时老眼昏花!看错了‘*’的位置。Sorry about that!!

if pi is a static member, the code should be as follows:
class A  
{  
   public:  
     int i; 
     static int *pi; 
};  

int* A::pi=0;  // NOT int A::*pi = 0 !!

现在代码是:
class AClass 

    public: 
      int I1; 
      Show(){cout << I1 <<"\n"}
};

int AClass::*pI1=&AClass::I1;

void main() {...}

说明:pI1不是A的data member.它只是一般的指针变量,
而它的类型是"int A::*",表明指向A的data member.
eg 当pI1 = &x时,x应该是A的data member.
而int AClass::*pI1=&AClass::I1;把它分开写可能更清楚些.
eg.
void main()
{
...
// declear a normal pointer "pI1"
// and it is data type is "int AClass::*"
int AClass::* pI1;
// Assign a value to pI1;
pI1 = &AClass::I1;
...
int i;
// Error!! i is not a data member of AClass.
pI1 = &i;
}

Hope to help u. :)

--
孤身走我路...
其实,路,两个人一起走比一个人要好。
email: [email protected]

※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 139.87.93.157]
发信人: ago (ago), 信区: C
标  题: Re: 小问题-...
发信站: 网易虚拟社区 (Mon Jun 14 19:01:25 1999), 站内信件

你回答的非常清楚了.
我明白了.
本人还算精于java,希望有机会交流心得.

--
※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.97.228.249]
发信人: ago (ago), 信区: C
标  题: Re: 小问题-...
发信站: 网易虚拟社区 (Mon Jun 14 19:02:17 1999), 站内信件

忘了say thanks.

--
※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.97.228.249]
发信人: dunhill (天地一沙鸥), 信区: C
标  题: Re: 小问题-...
发信站: 网易虚拟社区 (Tue Jun 15 10:41:58 1999), 站内信件

【 在 ago (ago) 的大作中提到: 】
: class A
: {
:     public:
:         int i;
: }; 

: int A::*pi=0; 

: 这最后一句,的结果是给class A的定义中增加了一个属性吗? 
: A中没有定义int *pi, 
: 有了最后一句,A中多了一个int指针,属性? 

这个..., 我想你是知道这是指向成员变量或函数的指针,因为
你已经在看书嘛,差的只不过是理解问题。我来一步一步引导阐
述,让你xx开朗。

先说一点基本的。假设有一个类定义如下:

#include "iostream.h"

class CTestA {
private:
    int m_iValue;
public:
    char *m_pszValue;
    void SetValue(int i) {
        m_iValue = i;
    }
    int GetValue() {
        return m_iValue;
    }
    virtual void PrintValue() {
        cout << m_iValue;
}
};
那么,CTestA的存储结构是怎样的呢?当你用VC编译器时(我不
知道是否所有的编译器表现都一样), 一个实例化的对象在内
存中应如下保存,假设对象名为testA:
|------------|
| m_pszValue |
|------------| 偏移 8
| m_iValue |
|------------| 偏移 4
| __vfptr |
testA->+------------+  偏移 0
__vfptr是指向虚函数表的指针,是实现多态性的主要机制。 由
此可见,该类的实例只占用了12个字节,而没有保存函数SetValue
和GetValue的指针。那么,当你执行
    testA.SetValue(100);
时,程序如何知道testA对象的SetVaule成员函数的地址呢? 该
对象中并没有储存该函数的入口地址呀?xixi, 这个不用担心,
因为编译的时候已经直接用函数的地址,  用x86汇编代码表示,
大概如下吧:
    push   100
    lea    eax, &testA  
    push   eax
    call   CTestA::SetVaule
    add    esp, 8           ??对不对??
不好,好像跑题了。知道了上面的东西,就可以更好地理解指向
类成员的指针。 在 c 中, 指针应该可以指向任何存储对象吧?
或者说,任何存储对象都可以用指针操作吧?(我不知道这样说
严不严谨,但我是这样理解的)。既然类的对象,对象的成员变
量,成员函数,虚函数等等总在内存中的某位置,所以,应该总
是可以构造一个指针指向他们。


指向对象成员变量的指针
======================

最基本地,定义一个void*指针,如:
    void *pvoid = &testA;
哈哈,这样的代码编译器竟然连警告也没有!这样,不论对象的
什么类型的成员变量, 包括private的,你都可以通过这个指针
任意访问,完全违反面向对象的数据封装性。因为void指针可以
转换为任何指针。:)

再来,
    pvoid = &testA.m_iValue;  //不能编译,不能访问私有变量
    pvoid = &testA.m_pszValue;//通过编译,没有警告
可见,对象的成员变量你可以任意指定指针指向他们,只要有访
问权限。可以试验出,对静态成员变量也可以如此访问。

结论1: 指向对象的成员变量的指针只要有访问权限即可自由使
        用,其形式如前所示。


指向类成员变量的指针
====================

类,当然不是一个对象,指向类成员变量的指针,需要指明类域,
如需安排一个指针指向CTestA::m_iValue,可以如下定义:
    int CTestA::*pi = &CTestA::m_iValue;
然后,对于对象testA,testA.*pi等价于testA.m_iValue。注意
这里“.*”操作符,类似地,还有“->*”操作符, 叫做“指向
成员指针操作符”,是专门这种用处的。

这里的pi与前面的pvoid有何不同? 前面的pvoid是专属于testA
这个对象的,而这里的pi是属于CTestA这个类的,所以引用这个
指针需加上对象标示域,如“testA”。

pi这个指针的值,实际上等于 m_iValue 相对于类存储位置的偏
移量,本例中,应该等于4。

    
指向类成员函数的指针
====================

指向成员函数的指针与指向成员变量的指针相当类似,不过定义
的时候要注意参数匹配。如:
    void (CTestA::*pf)(int) = &CTestA::SetValue;
所以,你既可以通过
    testA.SetVaule(100);
也可以通过
    (testA.*pf)(100);
来做同样的事情。
值得注意的是,pf的指针所指的真的是CTestA::SetValue函数入
口地址吗?你调试的时候看看汇编码,知道并不是这样。pf指向
的是一条跳转到SetValue入口的指令。为什么要这样间接一下呢?
我怀疑是减少代码装入时的重定位初始化。  可以这样考虑,假
设程序重要调用100次SetValue,因为装入代码时, 所有的调用
都要用SetValue函数的实际地址去重定位,所以, 要花费100次
重定位,  而采用这种方法, 只需一次重定位----修改jmp指令。
而且,pe文件头的重定位表也小的多。

写到这里,我对自己的表达能力越来越没有信心,希望你看了会
有点帮助,最好不要越看越糊涂。:))))))))))))

--
我是一沙鸥,天地任遨游。飞到昆仑边,飞到海尽头。
狂风我不怕,烈日我不愁。笑看黄鹂鸟,檐头意踌躇。

※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 210.75.46.91]
发信人: ago (ago), 信区: C
标  题: CrazyJava!!! are you here?
发信站: 网易虚拟社区 (Tue Jun 15 08:59:27 1999), 站内信件
我在看下面的话时有疑问,才引出我的问题,
我的问题的焦点在
"derived type pDAT".
// Define a derived type pDAT that points to  
// I1 members of objects of type AClass. 
int AClass::*pDAT = &AClass::I1; 
照理说,应该理解为,定义了一个派生类型 pDAT 指向AClass 的I1,
可是在使用上,pDAT完全象是一个AClass的属性.
AClass a,b;
a=...;
b=..;
...
a.*pDAT=1;
b.*pDAT=2;
这样用非常好用,我想知道,pDAT是程序中的'一个'变量?
还是'每一个'AClass中多了一个属性,
或者是有其他的解释?
--
※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.97.228.249]
发信人: vcc (vcc), 信区: C
标  题: Re: CrazyJava!!! are you here?
发信站: 网易虚拟社区 (Tue Jun 15 11:24:18 1999), 站内信件
【 在 ago (ago) 的大作中提到: 】
: 我在看下面的话时有疑问,才引出我的问题,
: 我的问题的焦点在
: "derived type pDAT".

:    .......
其实这相当于添加了类的一种类型安全的指针操作的方法。
看它的定义和使用可以明白,相当于类添加了*pMyType的方法。
1) 
Operator or Construct - ::* 
Syntax - type::*ptr-name 
Use - Declaration of pointer to member. The type  
      specifies the class name, and ptr-name  
      specifies the name of the pointer to member.  
      Pointers to members can be initialized.  
      For example: MyType::*pMyType = &MyType::i; 
2)  
Operator or Construct - .* 
Syntax - obj-name.*ptr-name 
Use - Dereference a pointer to a member using an  
      object or object reference. For example:  
      int j = Object.*pMyType; 
--
※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.103.146.7]
发信人: dunhill (天地一沙鸥), 信区: C
标  题: Re: CrazyJava!!! are you here?
发信站: 网易虚拟社区 (Tue Jun 15 11:38:10 1999), 站内信件
【 在 ago (ago) 的大作中提到: 】
: 我在看下面的话时有疑问,才引出我的问题,
: 我的问题的焦点在
: "derived type pDAT".
: // Define a derived type pDAT that points to   
: // I1 members of objects of type AClass.  
: int AClass::*pDAT = &AClass::I1;  
: 照理说,应该理解为,定义了一个派生类型 pDAT 指向AClass 的I1, 
: 可是在使用上,pDAT完全象是一个AClass的属性. 
: AClass a,b; 
: a=...; 
: b=..; 
: ... 
: a.*pDAT=1; 
: b.*pDAT=2; 
: 这样用非常好用,我想知道,pDAT是程序中的'一个'变量? 
: 还是'每一个'AClass中多了一个属性, 
: 或者是有其他的解释? 
你们的贴子我都看了。事实是这样,就不用说了, 是不是可以
这样理解,pDat不是AClass的一个属性,而是程序的一个变量,
这个变量是个指针,这个指针至向整形,这个整形是A类的成员。
哈哈,归根结底,就是指向“类”成员的指针。如果类A有多个
整形的成员,那么,pDat可以指向任何一个,所以, 在实际上
也还是有用的,虽然很少用。
--
我是一沙鸥,天地任遨游。飞到昆仑边,飞到海尽头。
狂风我不怕,烈日我不愁。笑看黄鹂鸟,檐头意踌躇。
※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 210.75.46.91]
发信人: crazyjava (骄傲的中国人), 信区: C
标  题: Re: CrazyJava!!! are you here?
发信站: 网易虚拟社区 (Tue Jun 15 12:53:18 1999), 站内信件
【 在 ago (ago) 的大作中提到: 】
: 我在看下面的话时有疑问,才引出我的问题,
: 我的问题的焦点在
: "derived type pDAT".

:    .......
在以前的贴子我也说了,在C++语言定义上,
pDAT只是一般变量,而不是Aclass的data member。
为了方便理解,你自己可以这样认为。但前提是要紧记
定义,这样才不会使用错。
MSDN已经指出了使用它的原因: 
"Use of pointers to class members enhances the  
 type safety of the C++ language." 
--
孤身走我路...
其实,路,两个人一起走比一个人要好。
email: [email protected]
※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 139.87.93.212]



[关闭][返回]