面向对象的三大特征=封装性+继承性+多态性
封装=将客观事物抽象成类,每个类对自身的数据和方法实行权限的控制
继承=实现继承+可视继承+接口继承
多态=将父类对象设置成为和一个或者更多它的子对象相等的技术,
用子类对象给父类对象赋值之后,
父类对象就可以根据当前赋值给它的子对象的特性一不同的方式运作
C++的空类有哪些成员函数
1.缺省构造函数
2.缺省拷贝构造函数
3.缺省析构函数
4.缺省赋值运算符
5.缺省取址运算符
6.缺省取址运算符const
PS:只有当实际使用的时候才会去使用这些类的成员函数的时候,编译器才回去定义它们。
拷贝构造函数和赋值运算符重载有以下两个不同之处:
1.拷贝构造函数生成的新的类对象,而赋值运算符不能;
2.由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用
检验源对象是否和新建对象相同。而赋值运算符则需要这个操作,另外赋值运算
中如果原来的对象中有内存分配要先把内存释放掉。
PS:当类中有指针类型的成员变量时,一定要重写拷贝构造函数和赋值运算符,不要使用默认的。
#include <iostream>
#include <stdio.h>
using namespace std;
#ifndef __GNUC__
#define __attribute__(x)
#endif // __GNUC__
__attribute__((constructor)) void before_main()
{
cout << __FUNCTION__ << endl;
cout << "这是在Main函数运行前执行的!" << endl;
}
__attribute__((destructor)) void after_main()
{
cout << __FUNCTION__ << endl;
cout << "这是在Main函数结束后执行的!" << endl;
}
对C++了解的人都知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。
在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证真实反应实际的函数。
这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以当用父类的指针来操作一个子类的时候,
这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
在C++标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置,这是为了保证正确取到虚函数的偏移量。
这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
//访问基类的私有虚函数
class A
{
public:
virtual void g()
{
cout << "A:g()" << endl;
}
virtual void f()
{
cout << "A::f()" << endl;
}
virtual void h()
{
cout << "A::h()" << endl;
}
};
/*
这类的虚函数表的排布方式是
vptr
|
| &(A::g())
|_ _ _ _ \&(A::f())
/&(A::h())
*/
class B:public A
{
public:
virtual void g()
{
cout << "B::g()" << endl;
}
virtual void h()
{
cout << "B::h()" << endl;
}
};
/*
这个类的虚函数表是
vptr
|
| &(A::g())-->&(B::g())
|_ _ _ _ \&(A::f())
/&(A::h())-->&(B::h())
*/
/*
Mark 一下:
每一个具有虚函数的类都有一个虚函数表VTABLE,
里面按在类中声明的虚函数的顺序存放着虚函数的地址,
这个虚函数表VTABLE是这个类的所有对象所共有的,
也就是说无论用户声明了多少个类对象,
但是这个VTABLE虚函数表只有一个。
在每个具有虚函数的类的对象里面都有一个VPTR虚函数指针,
这个指针指向VTABLE的首地址,每个类的对象都有这么一种指针。
*/
class C
{
public:
C() { cout << "C Constructor !" << endl; }
~C() { cout << "C Destroy !" << endl; }
};
//会在main函数调用之前执行的函数
static C s_c;
typedef void (*PFUNC)(void);
class AA
{
private:
int a;
double b;
public:
virtual void func()
{
cout << "AA::func()" << endl;
}
};
/*
在内存中的存储格式是
VPTR-->a-->b
|
|__\ &func()
/ &func()
*/
int main()
{
cout << __FUNCTION__ << endl;
cout << "Hello world!" << endl;
/*
很多人找不到vptr这个指针,其实这个B类继承自A类,且没有自己的成员变量,
所以可以得出,B类的事例对象中的只有vptr这一个指针了。
*/
B b;
PFUNC pf;
//先打印出虚函数表的地址
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表第一个函数地址:" << (int*)(*(int*)(&b)) << endl;
for(int i=0;i<3;i++)
{
pf=(PFUNC)*((int*)*(int*)(&b)+i);
pf();
}
return 0;
}