对象、多态和空类的大小
什么是对象
类就是拥有相等功能和相同的属性的对象的集合 -- 抽象的
对象就是类的实例 -- 具体
举个例子:
class A { /* 这里定义一些成员数据和一些操作,但是并没有指定使用这些数据和操作的“人”(实例或对象),所以说类是抽象的 */ } //这里指明了类A的使用对象是obj,是具体的 A obj;
面向对象的三大特征:
封装:找到变化并且把它封装起来,你就可以在不影响其它部分的情况下修改或扩展被封装的变化部分,这是所有设计模式的基础,就是封装变化,
因此封装的作用,就解决了程序的可扩展性。
继承:子类继承父类,可以继承父类的方法及属性,实现了多态以及代码的重用,因此也解决了系统的重用性和扩展性,但是继承破坏了封装,
因为他是对子类开放的,修改父类会导致所有子类的改变,因此继承一定程度上又破坏了系统的可扩展性,所以继承需要慎用,
只有明确的IS-A关系才能使用,同时继承在在程序开发过程中重构得到的,而不是程序设计之初就使用继承,很多面向对象开发者滥用继承,
结果造成后期的代码解决不了需求的变化了。因此优先使用组合,而不是继承,是面向对象开发中一个重要的经验。
多态:接口的多种不同的实现方式即为多态。接口是对行为的抽象,刚才在封装提到,找到变化部分并封装起来,但是封装起来后,怎么适应接下来的变化?
这正是接口的作用,接口的主要目的是为不相关的类提供通用的处理服务,我们可以想象一下。比如鸟会飞,但是超人也会飞,通过飞这个接口,
我们可以让鸟和超人,都实现这个接口,这就实现了系统的可维护性,可扩展性。
C++多态的实现
1、多态分为静态多态(函数重载和泛型编程)和动态多态(虚函数);
2、静态多态和动态多态的实际区别就是函数地址是早绑定还是晚绑定。如果函数调用时在编译期间就可以确定函数的调用地址并产生代码,就是静态的,也就是说地址是早绑定的(函数重载和泛型编程)。
而如果函数调用地址不能在编译期间确定,需要在运行时才能确定,这就属于晚绑定(通过虚函数实现);
3、C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时基类指针或引用将会根据对象的实际类型来调用相应的函数。
如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数
类的大小
1、C++标准规定类的大小不为0,空类的大小为1,当类不包含虚函数和非静态数据成员时,其对象大小也为1。
当类里面含有空的构造函数和虚析构函数的时候,类的到小也为1-->虚析构函数的内存大小也为1
#include<iostream> using namespace std; class ClassA {}; class ClassB { ClassB() { } }; class ClassC { ~ClassC() { } }; class ClassD { ClassD() { } ~ClassD() { } }; int main() { //类大小 cout << sizeof(ClassA) << endl;//1 cout << sizeof(ClassB) << endl;//1 cout << sizeof(ClassC) << endl;//1 cout << sizeof(ClassD) << endl;//1 system("pause"); }
2、如果在类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针指向虚函数表VTable,在32位机器上,一个对象会增加4个字节来存储此指针,它是实现面向对象中多态的关键。而虚函数本身和其他成员函数一样,是不占用对象的空间的。
#include<iostream> using namespace std; class ClassA { virtual void GetNum(); }; class ClassB { virtual void GetNum1(); virtual void GetNum2(); }; int main() { //类大小 cout << sizeof(ClassA) << endl;// 4或8,也就是一个指针的大小 cout << sizeof(ClassB) << endl;// 4或8,不管有几个虚函数,都和一个虚函数大小一样 system("pause"); }
3、如果类里面包含静态成员数据,那么静态成员数据不占类内内存
#include<iostream> using namespace std; class ClassA { private: int a; }; class ClassB { private: int a; static int b; }; int main() { static int b=0; //类大小 cout << sizeof(ClassA) << endl;// 4 cout << sizeof(ClassB) << endl;// 4 system("pause"); }
我们来看下面一个例子: