构造函数调用顺序:基类构造函数->对象成员构造函数->派生类本身的构造函数,析构顺序与其相反。
注:
- 基类调用构造函数顺序与继承顺序有关,按继承顺序从左到右对基类进行构造。
- 若继承中有虚继承的,虚继承的优先构造。
- 局部对象,在退出程序块时析构
- 静态对象,在定义所在文件结束时析构
- 全局对象,在程序结束时析构
- 继承对象,先析构派生类,再析构父类
- 对象成员,先析构类对象,再析构对象成员
例一:
构造函数和析构函数的调用顺序
#include<iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"Create A"<<endl;
}
~A()
{
cout<<"Free A"<<endl;
}
};
class B
{
public:
B()
{
cout<<"Create B"<<endl;
}
~B()
{
cout<<"Free B"<<endl;
}
};
class C : public B, virtual public A
{
public:
C() : B(), A()
{
cout<<"Create C"<<endl;
}
~C()
{
cout<<"Free C"<<endl;
}
private:
B b;
A a;
};
int main()
{
C c;
return 0;
}
运行结果如下:
分析:有虚基类,先构造A,再构造B,然后调用对象成员的构造函数B和A,最后调用子类的构造函数。
例二:
同名隐藏:子类和父类方法同名,基类同名方法被隐藏,不可访问。
#include<iostream>
using namespace std;
class B
{
public:
void fun()
{
cout<<"B::fun()"<<endl;
}
void fun(int a)
{
cout<<"B::fun(int)"<<endl;
}
private:
int x;
};
class D : public B
{
public:
void fun() //同名隐藏,基类方法不可访问,包括重载的方法
{
cout<<"D::fun()"<<endl;
}
};
int main()
{
D d;
d.fun(); //同名隐藏,基类方法不可访问,访问子类的fun方法
//d.fun(1);//同名隐藏,基类方法不可访问,包括重载的方法,会报错
return 0;
}
例三:
用父类对象访问同名方法时,调用父类的方法。
#include<iostream>
using namespace std;
class B
{
public:
void fun()
{
cout<<"B::fun()"<<endl;
}
void fun(int a)
{
cout<<"B::fun(int)"<<endl;
}
private:
int x;
};
class D : public B
{
public:
void fun()
{
cout<<"D::fun()"<<endl;
}
};
int main()
{
B b;
b.fun(); //访问父类的fun()方法
b.fun(1);//访问父类的fun(int)方法
return 0;
}
例四:
将子类对象赋给父类对象,再用父类对象调用同名方法时,调用父类的方法。
#include<iostream>
using namespace std;
class B
{
public:
void fun()
{
cout<<"B::fun()"<<endl;
}
void fun(int a)
{
cout<<"B::fun(int)"<<endl;
}
private:
int x;
};
class D : public B
{
public:
void fun()
{
cout<<"D::fun()"<<endl;
}
};
int main()
{
B b;
D d;
b = d;
b.fun(); //访问父类方法
b.fun(1);//同样访问父类方法
return 0;
}
例五:
将子类对象赋给父类对象,不能用父类对象调用子类方法。
#include<iostream>
using namespace std;
class B
{
public:
void fun()
{
cout<<"B::fun()"<<endl;
}
void fun(int a)
{
cout<<"B::fun(int)"<<endl;
}
private:
int x;
};
class D : public B
{
public:
void fun()
{
cout<<"D::fun()"<<endl;
}
void list()
{
cout<<"D::list()"<<endl;
}
};
int main()
{
B b;
D d;
b = d;
b.list(); //不能访问
return 0;
}
赋值兼容规则 (参考:https://blog.csdn.net/tyegong/article/details/10406803 )
赋值兼容规则是指在需要基类对象的任何地方都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员,而且所有成员的访问控制属性也和基类完全相同。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。赋值兼容规则中所指的替代包括以下的情况:
1>派生类的对象可以赋值给基类对象。
2>派生类的对象可以初始化基类的引用。
3>派生类对象的地址可以赋给指向基类的指针。
在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
多态的实现前提:
1、父类方法是虚方法
2、子类中重写父类的该方法
注:
重写(覆盖):基类和子类中方法名相同,返回值相同,参数列表相同。
实现多态后,用基类对象调用该方法,调用的是基类的方法,而将子类对象的地址或者引用给父类指针后,用父类指针调用该方法,调用的是子类的方法。
- 基类对象调用重写的方法的例子如下:
#include<iostream>
using namespace std;
class A
{
public:
virtual void fun(int x)
{
cout<<"A::fun(int)"<<endl;
}
};
class B : public A
{
public:
virtual void fun(int x)
{
cout<<"B::fun(int)"<<endl;
}
};
int main()
{
A a;
B b;
a = b;
a.fun(1); //调用基类的方法
return 0;
}
运行结果如下:
- 基类引用调用重写方法的例子如下:
#include<iostream>
using namespace std;
class A
{
public:
virtual void fun(int x)
{
cout<<"A::fun(int)"<<endl;
}
};
class B : public A
{
public:
virtual void fun(int x)
{
cout<<"B::fun(int)"<<endl;
}
};
int main()
{
B b;
A &a = b;
a.fun(1); //调用子类的方法
return 0;
}
运行结果如下:
用基类指针调用重写方法的例子如下:
#include<iostream>
using namespace std;
class A
{
public:
virtual void fun(int x)
{
cout<<"A::fun(int)"<<endl;
}
};
class B : public A
{
public:
virtual void fun(int x)
{
cout<<"B::fun(int)"<<endl;
}
};
int main()
{
B b;
A *a = &b;
a->fun(1); //调用子类的方法
return 0;
}
运行结果如下:
隐藏:
隐藏是指子类的函数屏蔽了与其同名的基类函数,规则如下:
- 如果子类的函数与基类的函数同名,但是参数不同。此时,无论有无virtual关键字,基类的函数将被隐藏。(重载是针对一个类而言,注意区分概念)
- 如果子类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏。(覆盖是必须基类函数有virtual修饰)
试着判断下面子类和基类中同名函数的关系:
class A
{
public:
virtual void f(float x)
{
cout<<"A::f(float)"<<endl;
}
void g(float x)
{
cout<<"A::g(float)"<<endl;
}
void h(float x)
{
cout<<"A::h(float)"<<endl;
}
};
class B : public A
{
public:
virtual void f(float x)
{
cout<<"B::f(float)"<<endl;
}
void g(int x)
{
cout<<"B::g(int)"<<endl;
}
void h(float x)
{
cout<<"B::h(float)"<<endl;
}
};
基类函数A::f(float x)覆盖了子类函数B::f(float x)
基类函数A::g(float x)隐藏了子类函数B::g(int x)
基类函数A::h(float x)隐藏了子类函数B::h(float x)