C++和Java的继承与多态相关

概念

面向对象三大特征

  • 封装
  • 继承
  • 多态

多态

多态的实现

  • 方法的重载 编译时多态
  • 方法的覆盖 运行时多态

多态的必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象

Java

重载

在一个类中,参数个数不同或类型不同或顺序不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A {
public void fun(int num) {
System.out.println("int");
}
public void fun(int num, int num2) {
System.out.println("int int");
}
public void fun(double num) {
System.out.println("double");
}
}
public class Main {
public static void main(String[] args){
A a = new A();
a.fun(1);
a.fun(1,2);
a.fun(1.0);
}
}
1
2
3
int
int int
double

重写

父类与子类中,相同函数名和参数,相同返回值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class B {
public void fun() {
System.out.println("Father");
}
}
class D extends B{
@Override
public void fun() {
System.out.println("Child");
}
}
public class Main {
public static void main(String[] args){
B b = new B(); b.fun();
D d = new D(); d.fun();
B dd = new D(); dd.fun();
}
}
1
2
3
Father
Child
Child

隐藏

父类的静态方法被子类的同名静态方法隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class B {
static public void fun() {
System.out.println("Father");
}
}
class D extends B{
static public void fun() {
System.out.println("Child");
}
}
public class Main {
public static void main(String[] args){
B b = new B(); b.fun();
D d = new D(); d.fun();
B dd = new D(); dd.fun(); //pay attention!
}
}
1
2
3
Father
Child
Father

更好的写法

1
2
B.fun();
D.fun();

初始化顺序

  1. 静态对象(变量)优先于非静态对象(变量)。静态对象(变量)只能初始化一次。
  2. 父类优先于子类

顺序:父类静态、子类静态、父类非静态、父类构造、子类非静态、子类构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class B {
static {
System.out.println("Father static");
}
{
System.out.println("Father non-static");
}
B() {
System.out.println("Father constructor");
}
}
class D extends B{
static {
System.out.println("Child static");
}
{
System.out.println("Child non-static");
}
D() {
System.out.println("Child constructor");
}
}
public class Main {
public static void main(String[] args){
B d = new D();
System.out.println();
B dd = new D();
}
}
1
2
3
4
5
6
7
8
9
10
11
Father static //父类静态
Child static //子类静态
Father non-static //父类非静态
Father constructor//父类构造
Child non-static //子类非静态
Child constructor //子类构造
Father non-static
Father constructor
Child non-static
Child constructor

C++

父类指针指向子类对象

调用普通函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class B {
public:
void fun() {
cout << "Father" << endl;
}
};
class D:public B{
public:
void fun() {
cout << "Child" << endl;
}
};
int main() {
B *b = new B(); b->fun();
D *d = new D(); d->fun();
B *dd = new D(); dd->fun(); //pay attention
return 0;
}
1
2
3
Father
Child
Father //pay attention

调用函数根据指针类型确定,而不是根据指针实际指向的对象类型确定

调用虚函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class B {
public:
virtual void fun() {
cout << "Father" << endl;
}
};
class D:public B{
public:
virtual void fun() {
cout << "Child" << endl;
}
};
int main() {
B *b = new B(); b->fun();
D *d = new D(); d->fun();
B *dd = new D();dd->fun(); //pay attention
return 0;
}
1
2
3
Father
Child
Child //pay attention

只有通过基类的引用或者指针调用虚函数时,才能发生动态绑定。

腾讯秋招模拟题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Base {
public:
virtual void foo() {cout << "Base foo" << endl;}
void bar() {cout << "Base bar" << endl; foo();}
};
class Derive:public Base {
public:
void foo() {cout << "Derive foo" << endl;}
void bar() {cout << "Derive bar" << endl; foo();}
};
int main() {
Base *ptr = new Derive();
ptr->bar();
}
1
2
Base bar // 静态绑定
Derive foo // virtual修饰,动态绑定

隐藏

如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏。

如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有 virtual关键字。此时,基类的函数被隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class B {
public:
void fun() {
cout << "Father" << endl;
}
};
class D:public B{
public:
void fun(int num) {
cout << "Child" << endl;
}
};
int main() {
D d;
//d.fun(); error
d.fun(0);
return 0;
}