覆盖方法(overriding)
在 C++ 里,当我们需要在基类里提供一个通用的函数,但是在它的某个子类里,需要修改这个方法的实现,就要用到覆盖。
覆盖是存在类中,子类覆盖是从基类继承过来的方法(函数)。但是函数名、返回值、参数列表都必须和基类的方法相同。
C++ 可以让我们很容易实现这种既有共同特征又需要在不同的类里有不同实现的方法。简单的举个爪子说就是,动物都知道用嘴吃!那么吃我们就可以说是动物的一个共同特征(相当于基类里面实现吃的方法),但是我们知道不同动物会有不同的吃法。这个就是不同的实现方法。
将基类的方法,在子类重新申明一次。
借鉴小甲鱼的例程举例:(基本的我都是加了注释)
//覆盖
#include <iostream>
#include<string>
/声明定义/
///基类//
class Animal///定义一个类 基类
{
public:
Animal(std::string thename);//加构造器
void eat();
void sleep();
void drool();
protected: //这个类本身或者它的子类可以访问
std::string name;
};
///定义子类/
class Pig: public Animal//定义子类
{
public:
Pig(std::string thename);
void climb();
void eat();///实现覆盖,则在子类再一次的声明
};
class Turtle: public Animal
{
public:
Turtle(std::string thename);
void swim();
void eat();///实现覆盖,则在子类再一次的声明
};
Animal::Animal(std::string thename)//Animal构造器
{
name=thename;
}
基类方法(函数)//
void Animal::eat()//eat()方法(函数)
{
std::cout<< "Animal::eat I'm eatting!" <<std::endl;
}
void Animal::sleep()//sleep()方法(函数)
{
std::cout<< "Animal::sleep I'm sleeping!" <<std::endl;
}
void Animal::drool()//drool()方法(函数)
{
std::cout<<" Animal::drool 我正在流口水"<<std::endl;
}
/子类方法(函数)/
void Pig::climb()
{
std::cout<<"Pig::climb 我是猪,我爬树"<<std::endl;
}
Pig::Pig(std::string thename):Animal(thename)///Pig构造器继承Animal构造器
{
}
void Turtle::swim()
{
std::cout<<"Turtle::swim 我在游泳"<<std::endl;
}
Turtle::Turtle(std::string thename):Animal(thename)//Turtle构造器继承Animal构造器
{
}
覆盖方法///
void Pig::eat()//子类覆盖了基类的方法
{
Animal::eat();
std::cout<<name<< "正在猪食!\n\n";///由于访问控制protected的原因,所以只能在基类或者子类访问
}
void Turtle::eat()
{
Animal::eat();
std::cout<<name<< "正在吃鱼!\n\n";///由于访问控制protected的原因,所以只能在基类或者子类访问
}
//主函数//
int main()
{
Pig pig("小猪猪");//定义一个pig Pig类名 pig变量
Turtle turtle("小甲鱼");//定义一个turtle turtle类名 turtle变量
pig.eat();//可以理解为函数的调用
turtle.eat();
pig.sleep();
turtle.sleep();
pig.climb();
turtle.swim();
return 0;
}
发生覆盖的条件:借助博客文章参考:点击打开链接
1、“三同一不低” 子类和父类的方法名称,参数列表,返回类型必须完全相同,而且子类方法的访问修饰符的权限不能比父类低。
2、子类方法不能抛出比父类方法更多的异常。即子类方法所抛出的异常必须和父类方法所抛出的异常一致,或者是其子类,或者什么也不抛出;3、被覆盖的方法不能是final类型的。因为final修饰的方法是无法覆盖的。
4、被覆盖的方法不能为private。否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
5、被覆盖的方法不能为static。所以如果父类中的方法为静态的,而子类中的方法不是静态的,但是两个方法除了这一点外其他都满足覆盖条件,那么会发生编译错误。反之亦然。即使父类和子类中的方法都是静态的,并且满足覆盖条件,但是仍然不会发生覆盖,因为静态方法是在编译的时候把静态方法和类的引用类型进行匹配。
重载方法
借鉴小甲鱼的例程举例:(内容跟上面的差不多,修改了一些)
//重载方法
#include <iostream>
#include<string>
/声明定义/
///基类//
class Animal///定义一个类 基类
{
public:
Animal(std::string thename);//加构造器
void eat();
void eat(int eatcount);//方法重载
void sleep();
void drool();
protected: //这个类本身或者它的子类可以访问
std::string name;
};
///定义子类/
class Pig: public Animal//定义子类
{
public:
Pig(std::string thename);
void climb();
};
class Turtle: public Animal
{
public:
Turtle(std::string thename);
void swim();
};
Animal::Animal(std::string thename)//Animal构造器
{
name=thename;
}
基类方法(函数)//
void Animal::eat()//eat()方法(函数)
{
std::cout<< "Animal::eat I'm eatting!" <<std::endl;
}
void Animal::sleep()//sleep()方法(函数)
{
std::cout<< "Animal::sleep I'm sleeping!" <<std::endl;
}
void Animal::drool()//drool()方法(函数)
{
std::cout<<" Animal::drool 我正在流口水"<<std::endl;
}
/子类方法(函数)/
void Pig::climb()
{
std::cout<<"Pig::climb 我是猪,我爬树"<<std::endl;
}
Pig::Pig(std::string thename):Animal(thename)///Pig构造器继承Animal构造器
{
}
void Turtle::swim()
{
std::cout<<"Turtle::swim 我在游泳"<<std::endl;
}
Turtle::Turtle(std::string thename):Animal(thename)//Turtle构造器继承Animal构造器
{
}
/重载方法
void Animal::eat(int eatcount)
{
std::cout<<"我吃了"<<eatcount<<"碗饭\n\n";
}
//主函数//
int main()
{
Pig pig("小猪猪");//定义一个pig Pig类名 pig变量
Turtle turtle("小甲鱼");//定义一个turtle turtle类名 turtle变量
pig.eat();//可以理解为函数的调用
turtle.eat();
pig.eat(10);//给一个参数传进去,会自动使用带有参数的方法
pig.sleep();
turtle.sleep();
pig.climb();
turtle.swim();
return 0;
}
注意的是:对从基类继承来的方法进行重载,程序永远编译不过的。
总结
方法的覆盖和重载具有以下相同点:
都要求方法同名
都可以用于抽象方法和非抽象方法之间
方法的覆盖和重载具有以下不同点:
方法覆盖要求参数列表(参数签名)必须一致,而方法重载要求参数列表必须不一致。
方法覆盖要求返回类型必须一致,方法重载对此没有要求。
方法覆盖只能用于子类覆盖父类的方法,方法重载用于同一个类中的所有方法(包括从父类中继承而来的方法)
方法覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。
父类的一个方法只能被子类覆盖一次,而一个方法可以在所有的类中可以被重载多次。