覆盖方法和重载方法
覆盖方法:
通过之前的学习,我们已经知道了如何通过创建新的子类来重用现有的代码(继承)。
虽然这个方案可以让我们轻松解决许多现实世界里的问题,但在某些场合,却又显得不够用。
例如当我们需要在基类里提供一个通用的函数,但在它的某个子类里需要修改这个方法的实现,在 C++ 里,覆盖(overriding)就可以做到。
回到我们之前的例子,我们都知道,但凡是个动物都知道吃!那么吃我们就可以说是动物的一个共同特征,但我们知道不同动物会有不同的吃法。。。。。。
C++ 可以让我们很容易实现这种既有共同特征又需要在不同的类里有不同实现的方法。
我们需要做的是在类里重新声明这个方法,然后再改写一下它的实现代码(就像它是一个增加的方法那样)就行啦。
修改例题:为我们的 Animal 添加 eat()方法,并在Pig 和 Turtle 中覆盖。
#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(); // new! }; class Turtle : public Animal { public: Turtle(std::string theName); void swim(); void eat(); // new! }; Animal::Animal(std::string theName) { name = theName; } void Animal::eat() { std::cout << "I'm eatting!" << std::endl; } void Animal::sleep() { std::cout << "I'm sleeping!Don't disturb me!\n" << std::endl; } void Animal::drool() { std::cout << "我是公的,看到母的我会流口水,我正在流口水。。。\n" << std::endl; } Pig::Pig(std::string theName) : Animal(theName) { } void Pig::climb() { std::cout << "我是一个只漂亮的小母猪猪,我会上树,我正在爬树,嘘。。。\n" << std::endl; } void Pig::eat() { Animal::eat(); std::cout << name << "正在吃鱼!\n\n"; // new! } Turtle::Turtle(std::string theName) : Animal(theName) { } void Turtle::swim() { std::cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里。。哈哈。。\n" << std::endl; } void Turtle::eat() { Animal::eat(); std::cout << name << "正在吃东坡肉!\n\n"; // new! } int main() { Pig pig("小猪猪"); Turtle turtle("小甲鱼"); // std::cout << "这只猪的名字是: " << pig.name << std::endl; // 错误 原因:protected // std::cout << "每只乌龟都有个伟大的名字: " << turtle.name << std::endl; // 错误 原因:protected pig.eat(); turtle.eat(); pig.climb(); turtle.swim(); return 0; }
I'm eatting! 小猪猪正在吃鱼! I'm eatting! 小甲鱼正在吃东坡肉! 我是一个只漂亮的小母猪猪,我会上树,我正在爬树,嘘。。。 我是一只小甲鱼,当母猪想抓我的时候,我就游到海里。。哈哈。。 请按任意键继续. . .
重载方法
简化编程工作和提高代码可读性的另一种方法是对方法进行重载。
重载机制使你可以定义多个同名的方法(函数),只是它们的输入参数必须不同。(因为编译器是依靠不同的输入参数来区分不同的方法)
重载并不是一个真正的面向对象特性,它只是可以简化编程工作的捷径,而简化编程工作正是 C++ 的全部追求! 我们来测试下对 eat()方法进行重载:test
#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) { name = theName; } void Animal::eat() { std::cout << "I'm eatting!" << std::endl; } void Animal::eat(int eatCount) { std::cout << "我吃了" << eatCount << "碗馄饨!\n\n"; } void Animal::sleep() { std::cout << "I'm sleeping!Don't disturb me!\n" << std::endl; } void Animal::drool() { std::cout << "我是公的,看到母的我会流口水,我正在流口水。。。\n" << std::endl; } Pig::Pig(std::string theName) : Animal(theName) { } void Pig::climb() { std::cout << "我是一个只漂亮的小母猪猪,我会上树,我正在爬树,嘘。。。\n" << std::endl; } Turtle::Turtle(std::string theName) : Animal(theName) { } void Turtle::swim() { std::cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里。。哈哈。。\n" << std::endl; } int main() { Pig pig("小猪猪"); Turtle turtle("小甲鱼"); // std::cout << "这只猪的名字是: " << pig.name << std::endl; // 错误 // std::cout << "每只乌龟都有个伟大的名字: " << turtle.name << std::endl; // 错误 pig.eat(); turtle.eat(); pig.eat(15); pig.climb(); turtle.swim(); return 0; }
I'm eatting! I'm eatting! 我吃了15碗馄饨! 我是一个只漂亮的小母猪猪,我会上树,我正在爬树,嘘。。。 我是一只小甲鱼,当母猪想抓我的时候,我就游到海里。。哈哈。。 请按任意键继续. . .
新手应该注意
对方法(函数)进行重载一定要有的放矢,重载的方法(函数)越多,程序就越不容易看懂。
在对方法进行覆盖(注意区分覆盖和重载)时一定要看仔细,因为只要声明的输入参数和返回值与原来的不一致,你编写出来的就将是一个重载方法而不是覆盖方法。这种错误以个人经验告诉你:往往很难调试!
对从基类继承来的方法进行重载,程序永远不会像你预期的那样工作!(try)
#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(int eatCount);//不能再继承之后,重载父类的方法 ,会造成覆盖 }; class Turtle : public Animal { public: Turtle(std::string theName); void swim(); }; Animal::Animal(std::string theName) { name = theName; } void Animal::eat() { std::cout << "I'm eatting!" << std::endl; } void Animal::sleep() { std::cout << "I'm sleeping!Don't disturb me!\n" << std::endl; } void Animal::drool() { std::cout << "我是公的,看到母的我会流口水,我正在流口水。。。\n" << std::endl; } Pig::Pig(std::string theName) : Animal(theName) { } void Pig::climb() { std::cout << "我是一个只漂亮的小母猪猪,我会上树,我正在爬树,嘘。。。\n" << std::endl; } void Pig::eat(int eatCount)//不能再继承之后,重载父类的方法 { std::cout << "我吃了" << eatCount << "碗馄饨!\n\n"; } Turtle::Turtle(std::string theName) : Animal(theName) { } void Turtle::swim() { std::cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里。。哈哈。。\n" << std::endl; } int main() { Pig pig("小猪猪"); Turtle turtle("小甲鱼"); // std::cout << "这只猪的名字是: " << pig.name << std::endl; // 错误 // std::cout << "每只乌龟都有个伟大的名字: " << turtle.name << std::endl; // 错误 pig.eat();//出错 turtle.eat(); pig.eat(15); pig.climb(); turtle.swim(); return 0; }