指向数据成员的指针,是一个有点神秘又颇有用处的语言特性,特别是如果你需要详细调查class members的底层布局的话。这样的调查可以用于决定vptr是放在class的起始处或者尾端。另外一个用途是可以用来决定class中的access sections的次序。
考虑下面的Point3d声明。其中有一个virtual function,一个static data member,以及三个坐标:
class Point3d{
public:
virtual ~Point3d();
//…
protected:
static Point3d origin;
float x,y,z;
}
每一个Point3d的对象含有三个坐标值,依次为x、y、z,以及一个vptr。至于静态数据成员origin,将被放在class object之外。唯一可能因编译器不同而不同的是vptr的位置。C++标准允许vptr被放在对象中的任何位置:在起始处,在尾端,或者是在各个members之间。然而实际上,所有编译器不是把vptr放在对象的头部,就是放在对象的尾部。
那么,取某个坐标成员的地址,代表什么意思呢?例如,以下操作所得到的值代表什么:
&Point3d::z;
上述操作将得到z坐标在class object中的偏移量(offset)。最低限度其值将是x和y的大小总和,因为C++语言要求同一个access level中的members的排列次序应该和其声明次序相同。在一台32位机器上,每一个float是4个字节,所以我们应该期望刚才获得的值要不是8,就是12(在32位机器上,一个vptr是4个字节)。
然而,这样的期望还少了1个字节。对于C和C++程序员来说,这多少算是个有点年代的错误了。如果vptr放在对象的末尾,则三个坐标值在对象布局中的偏移量分别为0、4、8;如果vptr放在对象的开头,则三个坐标值在对象布局中的偏移量分别为4、8、12。然而你若去取data members的地址,传回的值总是多1,也就是1、5、9或5、9、12等等。
#include <iostream>
class Point3d{
public:
virtual ~Point3d(){};
//…
public://如果换成private或者protected,则报错
static Point3d origin;
float x;
float y;
float z;
};
int main()
{
printf("&Point3d::x = %p/n", &Point3d::x);
printf("&Point3d::y = %p/n", &Point3d::y);
printf("&Point3d::z = %p/n", &Point3d::z);
std::cout<<"&Point3d::x = "<<&Point3d::x<<std::endl;
std::cout<<"&Point3d::y = "<<&Point3d::y<<std::endl;
std::cout<<"&Point3d::z = "<<&Point3d::z<<std::endl;
return 0;
}
输出结果为:
&Point3d::x = 00000004
&Point3d::y = 00000008
&Point3d::z = 0000000C
&Point3d::x = 1
&Point3d::y = 1
&Point3d::z = 1
Press any key to continue
在vc6.0下,并没有增加1,原因可能是visual c++做了特殊的处理。
在vc6.0下,通过printf或者cout的形式,都可以正常运行,只不过,得到的结果不一致。使用std::cout时,都输出的是1,应该作何解释呢?
以上程序,如果数据成员为private或者protected的,则无法编译通过,而书上的例子,却是protected,作者的测试程序可能是怎样的呢?
(以上程序在vc6.0,virsual studio2008,DEV-C++下测试过,与《深入探索C++对象模型》P131对应的说明有些出入)
为啥传回的值会多1个字节呢?这一个字节,主要用来区分“没有指向任何数据成员的指针”和“指向第一个数据成员的指针”这两种情况。考虑下面这样的例子:
float Point3d::*p1 =0;
float Point3d::*p2 = &Point3d::x;
//Point3d::* 的意思是“指向Point3d data member”的指针类型
if( p1 == p2 ){
cout <<” p1 & p2 contain the same value.”;
cout <<”they must address the same member!”<<endl;
}
为了区分p1和p2,每一个真正的member offset值都被加上1。因此,不论编译器或者使用者都必须记住,在真正使用该值以指出一个member之前,请先减去1。
在充分认识“指向数据成员的指针”之后,要解释:
&Point3d::z;和 &origin.z
之间的差异,就非常明确了:取一个非静态数据成员的地址,将会得到它在class中的offset,取一个绑定于真正class object身上的数据成员的地址,将会得到该数据成员在内存中真正的地址。&origin.z的返回值类型应该是:float * 而不是:float Point3d::* 。
#include <iostream>
class Point3d{
public:
virtual ~Point3d(){};
//…
public:
float x;
float y;
float z;
};
int main()
{
Point3d origin;
printf("&origin.z = %p/n", &origin.z);
return 0;
}
输出结果为:
&origin.z = 0013FF7C
转自:http://blog.csdn.net/wuliming_sc/article/details/3855574
分享到:
相关推荐
成员运算符(`.`)与指向结构体成员运算符(`->`)是C/C++语言中处理结构体数据类型时非常关键的两个运算符。它们虽然功能相似,但使用场景和语法结构上有着显著的不同。 首先,成员运算符`.`用于访问结构体变量的...
指向类的静态成员的指针,通过指针访问类的静态数据成员
指向数据成员的指针格式如下: <类型说明符><类名>::*<指针名> 指向成员函数的指针格式如下: <类型说明符>(<类名>::*<指针名>)(<参数表>) 例如,设有如下一个类A: class A { public: int fun (int b...
例如,如果我们有一个指向数据成员的指针`pimC`,我们可以通过`.`或`->*`操作符来访问成员。对于对象`aC`,我们使用`aC.*pimC`;对于对象`pC`的指针,我们使用`pC->*pimC`。这些操作符分别从对象或引用以及对象的...
通过使用指向结构体的指针,可以更灵活地访问和修改结构体中的成员。 **示例代码分析:** ```c typedef struct { char name[21]; char city[21]; char state[3]; } Rec; typedef Rec *RecPointer; RecPointer ...
C++ 中的指向类成员的指针可以分为两大类四小类,即指向数据成员还是成员函数,指向普通成员还是静态成员。下面对这四种类型的指针进行详细介绍。 一、指向类的普通成员的指针(非静态) 1. 指向类成员函数的指针 ...
结构体是由多个不同类型的数据成员组成的复合类型。当定义了一个结构体变量,它会在内存中占据一段连续的空间,每个成员都存储在这个空间的特定位置。结构体变量的指针就是这个内存段的起始地址。例如,我们可以定义...
同时,通过指针可以修改结构体内的成员,实现数据的更新。 3. **指向指针的指针示例.cpp**: 双重指针,也就是指向指针的指针,是一种高级特性,允许我们间接地操作指针本身。这种技术在动态内存分配、多级指针...
2. 指针作为结构体成员:结构体中可以包含指针成员,用于指向其他数据结构。 六、指针与数组 1. 数组名作指针:数组名实际上是一个指向数组首元素的指针,如`int arr[5]; int *p = arr;`,`p`指向数组的第一个元素...
在上面的`Testpm`类例子中,`pmfn`是一个指向成员函数`m_func1`的指针,而`pmd`是一个指向数据成员`m_num`的指针。使用`.*`和`->*`运算符,我们可以分别调用成员函数和访问数据成员,如`(ATestpm.*pmfn)()`和`...
C++中的成员函数指针是一种特殊的指针类型,它可以指向类的成员函数。在C++中,函数本身不是对象,因此不能直接赋值给普通指针。但是,成员函数指针允许我们存储对特定对象成员函数的引用,以便稍后调用。这种机制在...
在C++编程中,字符指针(char pointer)是一种常用的数据类型,用于处理字符串和文本数据。在面向对象编程中,特别是在派生类的设计中,字符指针可以扮演关键角色,帮助实现继承、多态性和封装等核心概念。标题“用...
例如,如果`ptr`是一个指向整型数据的指针,`*ptr`会返回`ptr`所指向的值。这被称为“解引用”操作。我们也可以通过指针来改变所指向的值,如`*ptr = 10;`将`ptr`所指向的内存位置的值设为10。 指针的另一个重要...
- 指向对象成员的指针则分为两种:指向数据成员的指针和指向成员函数的指针。前者用于指向类的特定数据成员,定义格式为:`<类型> <类名>::* <指针名> = <初值>`;后者用于指向类的特定成员函数,定义格式为:`...
指向函数的指针还可以用于更复杂的情况,例如作为其他函数的参数或作为类的成员。这样可以创建灵活的代码结构,允许在运行时根据需要调用不同的函数。例如,可以定义一个通用的处理函数,接受一个指向特定处理函数的...
这些成员函数可以是数据成员、成员函数或构造函数。成员函数指针提供了一种方法,使得可以在运行时动态地调用类的成员函数,这对于实现诸如事件处理机制或回调机制等非常有用。 #### 实现高性能的C++委托 **委托**...
例如,一个int*类型的指针指向的内存区域被解释为int类型的数据,char*指向的则是char类型的数据。 4. 指针的值和所指向的内存区域 指针的值是一个地址值,指针所指向的内存区域是根据指针的值所指向的地址开始的一...
指向函数的指针可以作为函数的参数,返回值,可以作为结构体的成员等。例如,int AddFunc(int (*f1)(int), int (*f2)(int), int a, int b){ return (*f1)(a) + (*f2)(b);}这里,AddFunc是一个函数,它的参数是两个...