对类层次的同名成员函数来说,有三种关系:函数重载(overload)、函数覆盖(override)、函数隐藏(hide、oversee)
一、函数重载
重载的概念相对简单,只有在同一类定义中的同名成员函数才存在重载关系,特点是函数的参数和数目有所不同
比如:
void output(int a,int b);
void output(int a);
简单的讲,就是函数名相同,但是参数类型或者参数数目不同,来看一个构造函数重载的例子
#include<iostream>
using namespace std;
struct Point
{
int x;
int y;
Point()//构造函数,注意没有任何返回值
{
x = 0;
y = 0;
}
Point(int _x, int _y) :x(_x), y(_y){}//重载构造函数
~Point()
{
cout << "再见" << endl;
}
void output()//成员函数
{
cout << "x="<<x << endl <<"y="<< y << endl;
}
};
int main()
{
Point p(5,10);//调用重载函数Point(int_x,int _y)
p.output();
system("pause");
return 0;
}
//加颜色
结果输出:
二、成员函数覆盖
指在派生类中覆盖基类中的同名函数,要求基类函数必须是虚函数,且:
1)与基类的虚函数(vittual)有相同的参数个数
2)与基类的虚函数有相同的参数类型
3)与基类的虚函数有相同的返回类型
特征如下:
1)不同的范围(分别位于派生类和基类)
2)相同的函数名字;
3)相同的参数
4)基类函数必须要有 virtual关键字。
仔细一看,覆盖不是和重载差不多么?区别如下:
1)覆盖发生在子类和父类之间;重载是同一个类中不同方法之间的关系
2) 覆盖要求参数列表相同,重载要求参数列表不同;覆盖要求返回类型相同,重找则不要求
3)覆盖是根据对象的类型来决定的,重载是根据调用时实参表与形参表来选择的
三、成员函数的隐藏
隐藏是指派生类中屏蔽了基类中的同名函数,包括
1)两个函数参数相同,但是基类不是虚函数。看到这大家应该比较清楚,和覆盖的区别就是基类函数是否是虚函数。
2)两个函数参数不同,无论基类是否是虚函数,基类函数都会被隐藏,和重载不同的是不在同一类中。
四、继承
这边来看看具体例子讲解,理解继承就是理解面向对象编程的关键,
我们定义一个动物Animal属性,有吃、睡觉、呼吸(动物都具有这些属性),
struct Animal//动物的属性 吃、睡觉、呼吸
{
void eat()
{
cout << "animal eat" << endl;
}
void sleep()
{
cout << "animal sleep" << endl;
}
void breathe()
{
cout << "animal sleep" << endl;
}
};
,现在 鱼需要继承这些属性,那么鱼也有吃、睡觉、呼吸,总不能再定义一个类吧,那么我需要鱼继承animal属性
struct Fish :public Animal//public代表类继承访问特性
{
};
再看main函数,上面Animal为父类,Fish为子类
int main()
{
Animal an;
an.eat();
Fish fh;
fh.sleep();
system("pause");
return 0;
}
以上程序输出结果,动物吃动物睡觉,这就是继承
五、继承的相关特性
上面代码中我们看到
struct Fish :public Animal//public代表类继承访问特性
{
};
有个public,那么自然有protected和private,关于这个如下表
六、构造函数的调用顺序
1、单继承时
派生时,构造函数和析构函数是不能继承的,所以派生类需要重新定义构造函数和析构函数,并且的构造函数的初始化列表中,调用基类的构造函数,简单讲,先调用 父类的构造,再调用子类的构造。析构时,先调用子类的析构,再调用父类的析构,
如下例子:
#include<iostream>
using namespace std;
struct Animal//动物的属性 吃、睡觉、呼吸
{
Animal()//父类的构造
{
cout << "animal construst" << endl;
}
~Animal()//父类的析构
{
}
void eat()
{
cout << "animal eat" << endl;
}
void sleep()
{
cout << "animal sleep" << endl;
}
void breathe()
{
cout << "animal sleep" << endl;
}
};
struct Fish :public Animal
{
Fish()//子类的构造
{
cout << "fish construct" << endl;
}
~Fish()//子类的析构
{
}
};
int main()
{
Fish fh;
system("pause");
return 0;
}
程序输出
来看看函数隐藏的情况
我们都知道,鱼睡觉是冒泡的,和动物不一样,来看看例子
#include<iostream>
using namespace std;
struct Animal//动物的属性 吃、睡觉、呼吸
{
Animal()//父类的构造
{
// cout << "animal construst" << endl;
}
~Animal()//父类的析构
{
}
void eat()
{
cout << "animal eat" << endl;
}
void sleep()
{
cout << "animal sleep" << endl;
}
void breathe()
{
cout << "animal sleep" << endl;
}
};
struct Fish :public Animal
{
Fish()//子类的构造
{
//cout << "fish construct" << endl;
}
~Fish()//子类的析构
{
}
void breathe()
{
cout << "fish bubu sleep" << endl;
}
};
int main()
{
Fish fh;
fh.breathe();
system("pause");
return 0;
}
输出
如果我们希望即有动物的睡觉又冒泡呢?,那么需要在fish这改变一下
struct Fish :public Animal
{
Fish()//子类的构造
{
//cout << "fish construct" << endl;
}
~Fish()//子类的析构
{
}
void breathe()
{
Animal::breathe();//添加父类的呼吸
cout << "fish bubu sleep" << endl;
}
};
输出结果既有动物的睡觉,又有鱼的冒泡
还需要注意的是fish对象内存布局,在赋值fish地址时,其实是Animal地址,如果想调用fish地址,需要加vitual,就是把函数定义成虚函数,这边涉及c++的多态性。
多继承时,派生类的构造函数初始化列表需要调用各自基类的构造函数,:
注意,此时派生类的构造函数的初始化列表只能控制用于初始化基类的值,不能控制基类的构造次序。
基类构造函数按照基类构造函数在类派生表中的出现次数调用。析构函数是按照构造函数的逆序进行