1.必须是公有继承
2.类中的函数必须为虚函数
3.必须使用指针和引用的方式进行。(->,&)
静态联编。,就是我们使用对象名调用我们的函数。函数名和对象的关系在编译阶段就已经确认。
联编是指计算机程序自身彼此关联的过程,是把一个标识符和存储地址连载在一起的过程,也就是把一个对象的操作相结合的过程。
如果使用基类指针或引用指明派生类对象,并使用该指针调用虚函数,则程序动态地选择派生类的虚函数,称为动态联编(运行时绑定),也叫滞后联编。
如:Obect为基类 Base为派生类
Object *op = *base op->fun() (动态选择虚函数)
如果使用对象名加.成员函数或者原则运算符“ ”引用特定的一个对象调用虚函数,则被调用的虚函数在编译时是确定的。则是静态联编。
如:
virtual void fun();
Object obj; obj.fun();
不管对象是够是虚的,均采用静态联编,因为对象和成员函数在编译器就已经绑定。
1.确认类型(class)
2.访问限定符
3.函数默认值(很重要)
(函数的默认值在编译结点就已经固定了。)
注意:
class Object
{
public:
virtual void print(int x = 20)
{
cout << "object ::print::x:" << x << endl;
}
};
class Base :public Object
{
public:
//普通成员函数
virtual void print(int a)
{
cout << "Base ::print::a:" << a << endl;
}
};
int main(void)
{
Base base;
Object* op = &base;
//op指向的base对象的地址。
op->print();
//所以op调用的是base中print()。
//但调用的值是在编译期就已经确定的。
return 0;
}

值在编译阶段,就已经确认。

调用的是派生类方法。
**
问题:

class Object
{
private:
int value;
public:
Object(int x = 0) :value(x) {}
virtual void add() { cout << "Object::add" << endl; }
virtual void fun() { cout << "Object::fun" << endl; }
};
class Base :public Object
{
private:
int num;
public:
Base(int x = 0) :num(x), Object(x + 10) {}
virtual void add() { cout << "Base::add" << endl; }
virtual void fun(int x) { cout << "Base::fun" << endl; }
};
int main()
{
Base base;
Object* op = &base;
Base* bp = &base;
op->fun();
op->add();
op->fun(12); error //无法访问,通过隐藏基类指针,查找派生类虚表,访问不到派生类中有,而基类中没有的虚方法。
op->Base::fun(12);error//访问不到,如下图:
bp->add();
bp->fun(); error //通过查表的方式,直接访问不到,但是可以通过加作用域范围,进行访问。因为派生类继承它没有
bp->Object::fun(); //而基类有的虚函数
bp->fun(10);
}

op能访问:add() fun()
bp能访问:add() Object::fun() fun(10)

class Object
{
private:
int value;
public:
Object(int x = 0) :value(x) {cout<<"create construct" <<endl;}
//~Object() {}
};
OBject *ip = new Object(10);
OBject *ip = new Object[10];
int main()
{
//创建了10个对象
Object* op2 = new Object[10];
delete[]op2;//ok
delete op2;//ok
free(op2);//ok
}
int main()
{
//创建一个对象
Object* op1 = new Object(10);
free(op1); //ok
delete(op1); //ok
delete[]op1; //ok
}
在没有析构函数时,不论申请多少个对象,free,delete detete [] 随便用
OBject *op1 = new Object(10);
在申请堆空间:有上越界和下越界标志。
free(op1); 和delete(op1);不会读取计数位置。
而 delete[]op1; 会把上越界当做计数位读取,从而导致错误。

OBject *op1 = new Object[10];
申请10个空间,还会申请计数位置和头部信息。如下图:
如果使用:free(op1); 和delete(op1);会导致读取头部信息失败。(因为不会把记录的个数也认为是头部信息,最终在读的时候出问题)
只有使用 delete[]op1; 才能够获取头部信息和计数位置。

free无法调动析构函数来析构对象,delete无法将记录的对象个个数也认为是头部信息,所以只能使用delete[]来释放多个对象。
如果是内置类型,就可以。
如果是自己设置的类型,要看有没有析构函数。
如果有,如果申请多个对象,就需要调用10次析构函数。
class Object
{
private:
int value;
public:
Object(int x = 0) : value(x) { cout << "Obejct::Create: " << this << endl; }
~Object() { cout << "Obejct::destroy: " << this << endl; }
};
class Base : public Object
{
private:
int num;
public:
Base(int x = 10) : Object(x + 10), num(x)
{
cout << "Base::Create: " << this << endl;
}
~Base() { cout << "Base::Destroy: " << this << endl; }
};
int main(void)
{
Object* op = new Base(10);
delete op;
return 0;
}

当有基类指针指向派生类的对象,如果基类和派生类都有析构函数,那么析构op指向的base对象时,使用的是基类的析构函数,。如果想连级调用base中的析构函数,必须将基类中的析构函数设置为虚函数,那么派生类中的析构函数就会覆盖掉基类中的析构函数,从而调用派生类中的析构函数。
采用虚析构:
virtual ~Object() { cout << "Obejct::destroy: " << this << endl; }
virtual~Base() { cout << "Base::Destroy: " << this << endl; }

重置虚表的意义:
当析构完Base中的资源,如果不把虚表指针指向Object的虚表,那么析构Object的时候,会再次析构Base中的add(),从而导致重复析构。
class Object
{
private:
int value;
public:
Object(int x = 0) : value(x) { cout << "Obejct::Create: " << this << endl; }
virtual void add() { cout << "Object::add" << endl; }
~Object()
{
add();
cout << "Obejct::destroy: " << this << endl; }
};
class Base : public Object
{
private:
int num;
public:
Base(int x = 10) : Object(x + 10), num(x)
{
cout << "Base::Create: " << this << endl;
}
virtual void add() { cout << "Object::add" << endl; }
~Base()
{
add();
cout << "Base::Destroy: " << this << endl; }
};
int main()
{
Object* op = new Base(10);
delete op;
return 0;
}

:调用object类中的构造函数,创建隐藏基类对象,让该对象内保存的虚表指针,指向虚表。即为置虚表
上面的程序;是先new一个base对象,先构造隐藏基类对象,使其内的虚表指针指向Object的虚表,当在此对base实例化时,基类的虚表指针会指向Base的虚表。
如下如:

在析构过程中,析构函数没第一部是先将虚表指针重新指向该对象的虚表。
因此会出现这个结果:
很明显,虚表指针指向Base的虚表时,析构base对象
虚表指针指向Object对象时,析构隐藏基类对象。
