记得之前总结C++学习心得的时候,我在函数重载的地方有一句话是函数的重载不能通过返回值来确定,但是有例外,我当时没有想起那个特例,今天在学习JAVA的时候看到JAVA里面函数返回值可以区分函数重载的时候我想起了C++中的那个特例。首先,我们来看一下JAVA中函数重载时通过函数返回值来区分函数的例子,源程序如下:
package polymorphism;
class Grain {
public String toString() {
return "Grain";
}
}
class Wheat extends Grain {
public String toString() {
return "Wheat";
}
}
class Mill {
Grain process() {
return new Grain();
}
}
class WheatMill extends Mill {
//和基类的process()函数构成重载
Wheat process() {
return new Wheat();
}
}
public class CovariantReturn {
public static void main(String[] args){
Mill m = new Mill();
Grain g = m.process();
System.out.println(g);
m = new WheatMill(); //之前的引用对象被丢弃,由垃圾回收器负责回收
g = m.process(); //基类的引用可以指向派生类对象,从而在下面打印时体现出多态
System.out.println(g);
}
}
上面JAVA源程序的输出结果如下:
Grain
Wheat
在上面的程序中我们看到基类Mill中函数的返回值类型是Grain,在其导出类中函数的返回值类型是Wheat,其中Wheat又是从Grain类中导出的,在WheatMill中两个process函数构成重载。在C++中我曾经也遇见过类似的情况,那么我们把上面的例子转换成C++code的话就写成下面的程序:
#include <iostream>
using namespace std;
class Grain
{
public:
void print()
{
cout << "Crain" << endl;
}
};
class Wheat : public Grain
{
public:
void print()
{
cout << "Wheat" << endl;
}
};
class Mill
{
public:
Grain& process()
{
return obj;
}
private:
Grain obj;
};
class WheatMill : public Mill {
public:
Wheat& process()
{
return obj;
}
private:
Wheat obj;
};
int main()
{
WheatMill objWm;
Mill objM;
objWm.process().print();
objM.process().print();
return 0;
}
编译运行结果如下:
Wheat
Grain
下面我们通过这例子来展示一下C++中的多态,先看一个错误的例子:
#include <iostream>
using namespace std;
class Grain
{
public:
void print()
{
cout << "Crain" << endl;
}
};
class Wheat : public Grain
{
public:
void print()
{
cout << "Wheat" << endl;
}
};
class Mill
{
public:
virtual Grain& process()
{
return obj;
}
private:
Grain obj;
};
class WheatMill : public Mill {
public:
virtual Wheat& process()
{
return obj;
}
private:
Wheat obj;
};
int main()
{
WheatMill objWm;
Mill objM;
objWm.process().print();
objM.process().print();
return 0;
}
不能把基类和派生类中的process函数声明为虚函数,这样不符合虚函数的书写规则,要想成为虚函数,基类中的函数是什么样子的在子类中也是什么样子的,但是在JAVA中不是这样规定的,上面的例子就是很好的证明。下面我们把print()函数声明为虚函数来体现多态性:
#include <iostream>
using namespace std;
class Grain
{
public:
virtual void print()
{
cout << "Crain" << endl;
}
};
class Wheat : public Grain
{
public:
virtual void print()
{
cout << "Wheat" << endl;
}
};
class Mill
{
public:
Grain process()
{
return obj;
}
private:
Grain obj;
};
class WheatMill : public Mill {
public:
Wheat process()
{
return obj;
}
private:
Wheat obj;
};
int main()
{
WheatMill objWm;
Mill objM;
Grain &objG1 = objWm.process();
objG1.print();
Grain &objG2 = objM.process();
objG2.print();
return 0;
}
此时程序的输出结果是:
Wheat
Grain
在写上面两个程序的时候,我得出了一个结论:即引用是不可以被复制的,复制编译器不会报错,但是引用还是指向定义时初始化的那个对象,下面我们就来证明一下这个结论:
先看下面的程序:
#include <iostream>
using namespace std;
class Grain
{
public:
virtual void print()
{
cout << "Crain" << endl;
}
};
class Wheat : public Grain
{
public:
virtual void print()
{
cout << "Wheat" << endl;
}
};
int main()
{
Grain obj1;
Wheat obj2;
Grain &obj3 = obj2;
obj3.print();
obj3 = obj1; //给引用再次赋值
obj3.print();
return 0;
}
输出结果:
Wheat
Wheat
再看下面一段程序:
#include <iostream>
using namespace std;
class Grain
{
public:
virtual void print()
{
cout << "Crain" << endl;
}
};
class Wheat : public Grain
{
public:
virtual void print()
{
cout << "Wheat" << endl;
}
};
int main()
{
Grain obj1;
Wheat obj2;
Grain &obj3 = obj1;
obj3.print();
obj3 = obj2; //给引用再次赋值
obj3.print();
return 0;
}
输出结果:
Grain
Grain
在上面两段程序中我们都做了相同的一件事:先定义一个引用并且初始化,然后再对引用进行赋值,结果发现引用的赋值没有效果,也就是程序不会按照我们预期的那样显示正确结果,这样我们就证明了之前的那个结论:引用的赋值没有意义。学习中不要放过任何有疑问的地方,不懂就要把不懂的地方弄明白想清楚,这样在思考一个问题的时候我们可以想到很多类似的问题,正所谓温故而知新。