C++虚函数实现机制以及类继承中的内存分布

发表于:2017-1-13 10:35

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:D_Guco    来源:51Testing软件测试网采编

  c++为了兼容c保留了struct类型,但是c++中的struct和c有明显的区别,c++中的struct可以继承,可以有成员函数,但是在c中却不行,在c++中struc和class更相似(还是有一些区别的,这里不再叙述),c中struct的内存分布很简单,那么c++中的class(struct)是怎么样的呢?
#include <iostream>
using namespace std;
class BaseClass
{
int a;
int b;
public:
void myfunc() {}
}
int main()
{
std::cout << "size = " << sizeof(BaseClass) << std::endl;
rturn 0;
}
  这里很简单运行结果为8,这里有一点要注意的就是static 变量存放在静态存储区,不占用该类的存储空间。这里还有一种特殊情况就是当c++为空类(可没有成员变量的),这时候c++为了确保每个对象有一个唯一的地址,给空类分了一个字节的大小来占位,对于此很多人很不理解,我们可以反向思维如果此时大小为0,那么一个类数组岂不是不占用空间了。
#include <iostream>
using namespace std;
class BaseClass
{
int a;
int b;
char c;
public:
virtual void myfunc() {}
}
int main()
{
std::cout << "size = " << sizeof(BaseClass) << std::endl;
rturn 0;
}
  这里的结果似乎有点意思(测试环境:mac 64),运行结果24,解释一下,首先两个int占8字节,最后就是这里的虚函数,这里稍微谈一下c++的虚函数,c++运行时多态是基于c++虚函数的,为了实现当父类指针指向一个自类对象时,根据对象的类型(子类还是父类)来执行相应的函数,c++在类中用一个int指针(官方称之为虚函数表,实际上是一个int数组,里面保存了所有的虚函数地址,注:以函数指针的形式保存),因此这个虚函数表的地址(int 指针)占用8字节,共24个字节。
#include <iostream>
using namespace std;
class BaseClass
{
public:
int a;
int b;
char c;
public:
virtual void myfunc() {}
};
class ChildClass : public BaseClass
{
public:
int d;
public:
virtual void myfunc() {}
};
int main()
{
std::cout << "BaseClass size = " << sizeof(BaseClass) << std::endl;
std::cout << "ChildClass size = " << sizeof(ChildClass) << std::endl;
rturn 0;
}
  这里运行结果一样都是24,当我点击运行看到运行结果的时候我的第一反应就是,我擦,系统出错了,子类比父类多了一个int竟然大小一样,我愣了一下,仔细分析了一下,才发现其中的奥秘,首先子类24,以上已经分析过了,这里之所以没有变化是因为子类把继承自父类的char c和自己的int d,合并成了5个字节然后做了8字节对齐,而子类是一个char 一个字节做了8字节对齐。
  这里可以看到,子类不但继承了父类的成员变量,而且继承了父类的的虚函数表,这时候问题来了,子类和父类怎么实现多态呢。
#include <iostream>
using namespace std;
class BaseClass
{
public:
int a;
int b;
public:
virtual void myfunc() {printf("BaseClass myfunc \n");}
virtual void myfunc1() {printf("BaseClass myfunc1 \n");}
};
class ChildClass : public BaseClass
{
public:
int d;
public:
virtual void myfunc() {printf("ChildClass myfunc \n");}
virtual void myfunc3() {printf("ChildClass myfunc3 \n");}
};
int main()
{
BaseClass *a = new BaseClass;
a->myfunc();
a->myfunc1();
printf("**************");
BaseClass *b = new ChildClass;
a->myfunc();
a->myfunc1();
//    b->myfunc3();
//    int myd = b->d;
printf("**************");
ChildClass *c = new BaseClass;
a->myfunc();
a->myfunc1();
rturn 0;
}
  运行结果:
  BaseClass myfunc
  BaseClass myfunc 1
  **************
  ChildClass myfunc
  BaseClass myfunc 1
  **************
  ChildClass myfunc
  BaseClass myfunc 1
  ChildClass myfunc3
  当创建父类对象时将虚表指针指向父类的虚表,当创建子类对象时,将虚表指针指向子类的虚表。所以当我们用一个父类指针指向一个子类对象时,通过该指针调用一个虚函数时,其实此时的虚表指针指向了子类的虚表,那么我们就会调用子类相应的虚函数了,当然这只是当子类重写了父类的虚函数,这时候就会有多态的机制,因为此时子类的虚函数表中指向的是自己的函数实现。如果我们子类没有实现父类的虚函数呢,因为我们继承了父类的虚函数表,所以我们回去调用父类的函数。
  还有一点要注意的就是当我们用一个父类指针指向一个子类对象时,能实现多态的必须是父类中有的,因为当我们子类继承了父类的成员函数和变量但是父类并没有子类的(上面注释掉的两行是错误的,注:这里有一个疑问,第二行注释我能理解,父类并没有子类的成员变量d,但是按我的理解此时调用虚函数时,应该指向了子类的虚函数表,此时虚函数表是子类的,为什么不能调用自己的myfunc3呢?求解....)
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号