本文主要研究下C++中的类型成员指针。
一、类型成员指针是什么鬼?
相信很多童鞋刚刚听到这个名字时,第一反应就是不就是类中的指针成员变量吗,有什么好研究的?错,而且是大错而特错,请注意这两种叫法的区别:前者是类型成员指针,后者是类指针成员。差之毫厘失之千里也。
我们声明了一个名叫person的类,类中声明了public的成员变量m_name和带参构造函数。而所谓的类型成员指针就是指向类中任意成员指针,它和类对象无关,也就是说它声明后可以被任意类对象进行调用。
它声明格式为:
std::string person::* ptr = &person::m_name;
std::string ,是我们需要为其指明的类成员指针的类的成员变量的类型。
person::*,表明这是一个类成员指针
ptr,是类成员指针的名称
&person::m_name,表明为哪个类的成员变量指定类成员指针
那么类成员指针怎么使用呢?请参考下面的main函数:
我想看到最终打印结果,大家也明白类型成员指针的作用了吧,那就是:
类型成员指针可以指向本类任意一个对象的该成员,并可以对成员进行读写操作。请注意这里是说的任意一个对象,也就是说类型成员指针和类对象无关。
执行上面的代码,你就会发现编译器会报下面的错误:
我们知道任何一个指针都可以被转换成void*,这就从一个侧面证实了类型成员指针不是指针,记住,这是证明类型成员指针不是指针的第一个理由。那么我们怎么才能把ptr的值打印出来呢?答案很无耻,用memcpy,接下来把代码改成下面的样子:
person中新增了一个int型的成员变量m_age,我们在mani函数中将会创建一个指向m_age的类型成员指针,然后打印类型成员指针的值:
大小为4字节,所有程序打印出来了0和4。
至此,我们证明了类型成员指针起始就是该成员距离类对象起始地址的偏移量,当我们用类对象去调用该类型成员指针时,程序就会用类对象地址加上改偏移量去解析其对应的值。
三 有类型成员函数指针吗?
当然存在!
我们为person类增加一个成员函数:
运行结果:
使用类的成员函数指针时需要注意:
1、定义类成员函数指针时指针前面必须加类名和命名空间限定符,也就是形如:person::*ppt;同时等号右边、类成员函数前面必须加上取地址操作符&。
2、使用类成员函数指针时必须把对象名和指针名括起来,形如:(p1.*ppt)
OK,以上就是对类成员函数指针(包括类成员变量指针和类成员函数指针)的简单研究和介绍。
一、类型成员指针是什么鬼?
相信很多童鞋刚刚听到这个名字时,第一反应就是不就是类中的指针成员变量吗,有什么好研究的?错,而且是大错而特错,请注意这两种叫法的区别:前者是类型成员指针,后者是类指针成员。差之毫厘失之千里也。
那么什么是成员类型指针呢?我们先看声明一个类,作为本次研究的小白鼠。
class person
{
public:
std::string m_name;
person(const char* name):m_name(name){}
};
我们声明了一个名叫person的类,类中声明了public的成员变量m_name和带参构造函数。而所谓的类型成员指针就是指向类中任意成员指针,它和类对象无关,也就是说它声明后可以被任意类对象进行调用。
它声明格式为:
std::string person::* ptr = &person::m_name;
std::string ,是我们需要为其指明的类成员指针的类的成员变量的类型。
person::*,表明这是一个类成员指针
ptr,是类成员指针的名称
&person::m_name,表明为哪个类的成员变量指定类成员指针
那么类成员指针怎么使用呢?请参考下面的main函数:
int main()
{
std::string person::* ptr=&person::m_name;
person p1("张三"),p2("李四");
std::cout<<p1.ptr<<std::endl;
std::cout<<p2.ptr<<std::endl;
return 0;
}
猜猜结果是什么?明显是
[Hyman@Hyman-PC classmemptr]$ g++ person.cpp
[Hyman@Hyman-PC classmemptr]$ ./a.out
张三
李四
我想看到最终打印结果,大家也明白类型成员指针的作用了吧,那就是:
类型成员指针可以指向本类任意一个对象的该成员,并可以对成员进行读写操作。请注意这里是说的任意一个对象,也就是说类型成员指针和类对象无关。
二 类型成员指针是指针吗?
这个问题抛出来一看就让人觉得是没事找抽型的,你都说了是类型成员指针了,难道它还不是指针?可事实是它本来就不是指针?我们继续做下面的试验,我们把ptr的值打印出来:
int main()
{
std::string person::* ptr=&person::m_name;
person p1("张三"),p2("李四");
std::cout<<p1.ptr<<std::endl;
std::cout<<p2.ptr<<std::endl;
std::cout<<(void*)ptr<<std::endl;
return 0;
}
执行上面的代码,你就会发现编译器会报下面的错误:
person.cpp: In function ‘int main()’:
person.cpp:18: 错误:从类型‘std::string person::*’到类型‘void*’的转换无效
我们知道任何一个指针都可以被转换成void*,这就从一个侧面证实了类型成员指针不是指针,记住,这是证明类型成员指针不是指针的第一个理由。那么我们怎么才能把ptr的值打印出来呢?答案很无耻,用memcpy,接下来把代码改成下面的样子:
int main()
{
int i;
std::string person::* ptr=&person::m_name;
person p1("张三"),p2("李四");
std::cout<<p1.ptr<<std::endl;
std::cout<<p2.ptr<<std::endl;
memcpy(&i,&ptr,4);
std::cout<<i<<std::endl;
return 0;
}
我们把ptr的值原样memcpy到i的内存中,然后打印i的值,也就是ptr的值,运行结果:
[Hyman@Hyman-PC classmemptr]$ ./a.out
张三
李四
0
什么?打印出来的i(ptr)的值竟然是0??这当然不会是地址,地址不都是一些0x....的东西吗?这是类型成员指针不是地址的第二个理由。那么类型成员指针到底是什么?答案是对象成员相对于对象地址的便宜量!为了验证这个结论,我们修改一下person类:
class person
{
public:
std::string m_name;
int m_age;
person(const char* name,int age):m_name(name),m_age(age){}
};
person中新增了一个int型的成员变量m_age,我们在mani函数中将会创建一个指向m_age的类型成员指针,然后打印类型成员指针的值:
int main()
{
int i;
std::string person::* ptr=&person::m_name;
int person::* ptr1=&person::m_age;
person p1("张三",28),p2("李四",30);
std::cout<<p1.ptr<<std::endl;
std::cout<<p2.ptr<<std::endl;
memcpy(&i,&ptr,4);
std::cout<<i<<std::endl;
memcpy(&i,&ptr1,4);
std::cout<<i<<std::endl;
return 0;
}
运行结果:
[Hyman@Hyman-PC classmemptr]$ ./a.out
张三
李四
0
4
我们知道person类对象在创建时,程序函数是按照顺序创建的,m_name先被创建出来,它和p1对象地址的偏移地址是0,然后m_age被创建出来,它和p1的地址中间隔着m_name,而string对象的
大小为4字节,所有程序打印出来了0和4。
至此,我们证明了类型成员指针起始就是该成员距离类对象起始地址的偏移量,当我们用类对象去调用该类型成员指针时,程序就会用类对象地址加上改偏移量去解析其对应的值。
三 有类型成员函数指针吗?
当然存在!
我们为person类增加一个成员函数:
class person
{
public:
std::string m_name;
person(const char* name):m_name(name){}
void print();
};
void person::print()
{
std::cout<<"hello c++"<<std::endl;
}
然后在main函数中调用声明并调用person类的成员函数指针:
int main()
{
void (person::*ppt)()=&person::print;
person p1("张三",28);
(p1.*ppt)();
}
运行结果:
[Hyman@Hyman-PC classmemptr]$ ./a.out
hello c++
使用类的成员函数指针时需要注意:
1、定义类成员函数指针时指针前面必须加类名和命名空间限定符,也就是形如:person::*ppt;同时等号右边、类成员函数前面必须加上取地址操作符&。
2、使用类成员函数指针时必须把对象名和指针名括起来,形如:(p1.*ppt)
OK,以上就是对类成员函数指针(包括类成员变量指针和类成员函数指针)的简单研究和介绍。