您的位置:首页 > 编程语言 > C语言/C++

C++/Java中继承关系引发的调用关系详解

2015-09-08 18:27 453 查看
C++:

这里引用到了 http://blog.csdn.net/haoel/article/details/1948051/ 中的内容,还请提前阅读陈大神的这篇博客后在阅读本篇。

覆盖,实现多态的基础,通过虚函数表来实现,下面这个例子便是覆盖 Override

#include<iostream>

using namespace std;

class Base{
public:
Base(){
cout << "Base::Base" << endl;
}

virtual void f(){
cout << "Base::f" << endl;
}
};

class Derived : public Base{
public:
Derived(){
cout << "Devried::Derived" << endl;
}

void f(){
cout << "Derived::f" << endl;
}
};

int main(){
Base *p = new Derived(); // Base::Base CRCL Derived::Derived
p->f(); // Derived::f
}


如果 Base类f()没有被virtual修饰,如下

#include<iostream>

using namespace std;

class Base{
public:
Base(){
cout << "Base::Base" << endl;
}

void f(){
cout << "Base::f" << endl;
}
};

class Derived : public Base{
public:
Derived(){
cout << "Devried::Derived" << endl;
}

void f(){
cout << "Derived::f" << endl;
}
};

int main(){
Base *p = new Derived();
p->f(); // Base::f
}


如果理解了陈大神博客上那些图,应该不会对这个结果产生意外,这是我画的一个草图,并不对应于真实的内存地址情况,只是为了方便说明概念,下面一排描述的是Derived:



解析:首先p是Base类型的指针,其对于p而言其能只能按照Base类型去运算偏移地址等来访问,所以其不可能访问到Derived中的函数。也就是说父类指针的作用域只有int a , Base::f Base::g这一部分。这样就能理解了。

隐藏,父类指针的访问域是父类部分,子类虽然拥有一份父类函数的拷贝,但如果子类中存在于父类同名的函数,则调用和变量类型一致的函数(即父类变量调用父类函数,子类变量调用子类函数,同时成对方的函数被隐藏)。例:

#include<iostream>

using namespace std;

class Base{

public:
int a;

Base(){
cout << "Base::Base" << endl;
}

void f(float a){
cout << "Base::f" << endl;
}
};

class Derived : public Base{
public:
int d;
Derived(){
cout << "Devried::Derived" << endl;
}

void f(int a){
cout << "Derived::f" << endl;
}
};

int main(){
Derived *p = new Derived();
p->f(3.14f); // Derived::f
}


这里虽然传入f的参数是float型,更适合调用Base::f,但根据同名优先调用子类的原则,实际调用为Derived::f。这就是父类同名方法被隐藏,如果指针类型为父类,则称子类方法被隐藏。

如果子类f定义为 void f() 这里对f传值就会编译不通过。

Java

覆盖,java的情况很简单,只要是子类含有和父类的同名方法,就是覆盖(无论子类的函数是否为abstract)。

public class Test {
public static void main(String[] args){
Base base = new Derived();
base.f(); // Derived::f
}
}

class Base {

public void f() {
System.out.println("Base::f");
}
}

class Derived extends Base {
public void f() {
System.out.println("Derived::f");
}
}


隐藏,Java中也存在隐藏,不过这个隐藏和C++不太一样,子类对象引用可以调用父类的同名函数

public class Test {
public static void main(String[] args){
Derived derived = new Derived();
derived.f(3); //Derived::f
derived.f(3.14f); // Base::f
}
}

class Base {

public void f(float a) {
System.out.println("Base::f");
}
}

class Derived extends Base {
public void f(int a) {
System.out.println("Derived::f");
}
}


再讲覆盖,C++父类函数中如果调用了自己的虚成员函数,那么由于这个虚成员函数被覆盖了,所以其实相当于调用了子类的函数。例:

#include<iostream>

using namespace std;

class Base{

public:
int a;

Base(){
cout << "Base::Base" << endl;
}

void f(){
g();
}

virtual void g(){
cout << "Base::g" << endl;
}
};

class Derived : public Base{
public:
int d;
Derived(){
cout << "Devried::Derived" << endl;
}

void g(){
cout << "Derived::g" << endl;
}
};

int main(){
Derived *p = new Derived();
p->f(); // Derived::g
}


Java中类似:

public class Test {
public static void main(String[] args){
Base base = new Derived();
base.f(); //Derived::f
}
}

class Base {

public void f() {
g();
}

public void g(){
System.out.println("Base::g");
}
}

class Derived extends Base {
public void g(){
System.out.println("Derived::g");
}
}


由于想利用这种特性,有人会犯一个错误,在父类的构造函数中调用被覆盖的函数,由于子类在构建时先调用父类的构造函数,此时子类为构造所以不能调到子类的覆盖方法,由此

产生了奇怪的错误。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: