系列文章目录
1.定义一个空类型,里面没有任何成员变量和成员函数,对该类型求sizeof,大小是多少?
答:1
code
class A
{
};
int main(){
A a;
std::cout << sizeof(a) << endl;
return 0;
}
output
1
解析
原本应该是0,但声明类型实例时,必须在内存中占有一定空间大小,否则无法使用。具体占多少取决于编译器。VS中每个空类型的实例占用1B。
2.如果该类型中定义一个构造函数和析构函数,求sizeof大小
答:1
code
class A
{
public:
A(){};
~A(){};
void foo(){}; //不止是构造和析构,任何非虚函数都一样
};
int main(){
A a;
std::cout << sizeof(a) << endl;
return 0;
}
output
1
解析
对于非虚成员函数,调用时只需要知道函数地址即可,这些函数地址只与类型相关,与类型无关,编译器不会在实例内添加额外信息。
3.如果将析构定义成虚函数呢?
答:8
code
class A
{
public:
A(){};
virtual ~A(){};
void foo(){}; //不止是构造和析构,任何非虚函数都一样
};
int main(){
A a;
std::cout << sizeof(a) << endl;
return 0;
}
output
8
解析
编译器一旦发现虚成员函数 ,会进行下面两个操作:
- 生成一个虚函数表(virtual table, vtbl),该表中存放一堆指向虚函数的指针。类所关联的type_info object(支持runtime type identification, RTTI)通常发在虚函数表的第一个槽内。
- 类对象被安插一个指针(vptr),指向相关的虚函数表。
而一个指针类型,在64位系统占8字节,32位系统中占4字节。
总结
以上内容改自《剑指offer》。
下面针对对象类型进行描述,看完下面内容,上面的3个问题就会豁然开朗。(参考《深度探索C++对象模型》)
首先我们定义这样一个类:
class Point{
public:
Point(float xval);
virtual ~Point();
float x() const;
static int PointCount();
protected:
virtual ostream& print(ostream &os) const;
float _x;
static int _point_count;
};
该类具有:
- 2个虚函数(析构 + print);
- 1个静态成员函数(PointCount);
- 2个非静态成员函数(构造函数 + x(),构造函数不能是虚函数)
- 1个非静态成员变量
- 1个静态成员变量
对于成员变量:
- 非静态成员变量置于类对象之内;
- 静态成员变量置于类对象之外;
对于成员函数:
- 静态和非静态成员函数都置于类对象之外;
- 虚成员函数则采用下面两个步骤处理:
- 生成一个虚函数表(virtual table, vtbl),该表中存放一堆指向虚函数的指针。类所关联的type_info object(支持runtime type identification, RTTI)通常发在虚函数表的第一个槽内。
- 类对象被安插一个指针(vptr),指向相关的虚函数表。
用下图说明上面问题
上面这张图一目了然地说明了前面的三个问题。理解该图就理解了前面三个问题。