本文根据动物类作为父类/基类, 猫狗类作为子类/派生类, 介绍整个面向对象过程中的重要知识点
文章目录
- 1.1 创建第一个父类 (动物类)
- 1.2 创建子类继承上面的父类 (猫狗继承动物类)
一. 继承
面向对象编程的最大功能, 继承是事务具有祖先特性, 事务也有自己的特性, 具体将: 继承机制允许派生类继承基类的属性和方法, 同时可以增加新的属性和方法
1.1 创建第一个父类 (动物类)
知识点包含: 构造函数 析构函数 重写 protected,public,private new/delete
class Animal // (1) 创建基类, 也就是父类, 名称为Animal
{
protected: // 关键字<保护属性只能给子类用, private只能父类自己用>
string name = "";
int food=0;
public: // 关键字<公开, 所有类都可以访问>
Animal(){ // (2) 构造方法, 创建这个类的对象, 直接使用Animal即可
printf("创建了一只 \n");
}
Animal(string name){ // 方法重写, 构造方法, 创建类使用Animal("猫"), 则会给这个对象里面的属性name直接赋值
const char *p = name.c_str();
printf("创建了一只 %s\n", p);
name = name;
}
~Animal(){ // (3) 析构函数, 用于在对象销毁的时候运行该函数
cout<<name;
cout<<" Animal对象已经被摧毁"<<endl;
}
void get_name(){ //(4) 创建方法得到本类的属性
cout<<"请求的name是: "<<name<<endl;
}
void set_food(int num){ //(5) 创建方法设置本类的属性
food = num;
}
void get_food(){
cout<<"请求的food是: "<<food<<endl;
}
// 创建虚方法, // (6) 也就是父类不实现, 子类实现
virtual string shot(){
return "";
}
};
int main(){
Animal* cat = new Animal("cat"); // 创建一个动物对象, 起名为cat
cat->get_name(); // 因为使用的是指针, 因此使用->符号
delete(cat); // 由于是new出来的空间, 需要使用delete函数销毁
Animal cat1("dog"); // 创建一个动物对象, 自动销毁
return 0;
}
1.2 创建子类继承上面的父类 (猫狗继承动物类)
知识点包含: 继承 继承的方法重写 获取继承的属性 构造子类对象时的构造顺序
class Cat:public Animal{ // (1) 通过:public Animal 继承
public:
Cat(string Catname){ // (2) 创建函数
cout<<"创建了一只猫:"<<Catname<<endl;
name = Catname;
};
// 增加override表示方法重写
string shot(){
cout<<name<<" 叫了 喵喵喵"<<endl;
return "喵喵喵";
}
};
class Dog:public Animal{ // 继承
public:
Dog(string Catname){
cout<<"创建了一只狗:"<<Catname<<endl;
name = Catname;
};
// 增加override表示方法重写
string shot(){
cout<<name<<" 叫了 汪汪汪"<<endl;
return "汪汪汪";
}
};
int main(){
Cat c("cat2"); // 在继承父类时, 先构造父类, 在构造子类
c.get_name(); // 调用父类方法, 返回父类中的属性
c.set_food(522);
c.get_food();
return 0;
}
二. 多态
___面向对象中的多态不同的对象收到相同的消息时产生多种不同的行为。实现多态的方法主要两种,一种是编译时多态,主要由重载和模板来实现;一种是运行时多态,主要由虚函数(包括继承)来实现。
___多态性增加了软件的灵活性以及重用性,为软件的开发和维护提供了极大的便利,尤其是采用了虚函数和动态连编机制后,允许用户更为明确,易懂的方式去建立通用的软件。
int main(){
Animal *a1; // 创建父类指针
Cat c1("cat2"); // 创建子类对象
a1 = &c1; // 父类指针指向子类对象
a1->get_name(); // 父类指针调用子类对象的
}
2.1 动物歌唱大赛
我们想象一个场景, 当举办一个动物歌唱大赛, 也就是所有动物都要用到Animal.shot()
方法, 虽然登记表填写的是不同的动物, 但是比赛的时候可不管你是哪个动物, 都是把你当成Animal
进行歌唱的, 这时候, 我们就用到了泛型, 使用父类指向子类, 用父类指针调用子类的方法, 这样父类都是动物, 所有的行为就统一了, 但是由于指向的子类不一样, 造成的结果也是不一样的;
具体实现如下:
int main(){
Animal *animal[5]; // 创建一个表, 表大小是5
animal[0] = new Cat("神猫"); // 第一个动物 猫 名字是神猫 报名, 在表中的第一行
animal[1] = new Dog("神狗"); // 第一个动物狗, 给这位选手配置号码为1
animal[2] = new Cat("九月"); //给这个选手配置号码为2
animal[3] = new Cat("天阙"); // 不管你是什么动物, 我都把你看成是个动物
animal[4] = new Dog("朱砂");
// 随着一声令下 比赛开始, 所有动物一起shot, 最后狗赢了
for(Animal *item : animal){
item->shot();
}
// 一定要记住, new出来的东西一定要进行销毁, 不然容易尴尬
for(Animal *item : animal){
delete(item);
}
return 0;
}
/*
创建了一只
创建了一只猫:神猫
创建了一只
创建了一只狗:神狗
创建了一只
创建了一只猫:九月
创建了一只
创建了一只猫:天阙
创建了一只
创建了一只狗:朱砂
神猫 叫了 喵喵喵
神狗 叫了 汪汪汪
九月 叫了 喵喵喵
天阙 叫了 喵喵喵
朱砂 叫了 汪汪汪
神猫 Animal对象已经被摧毁
神狗 Animal对象已经被摧毁
九月 Animal对象已经被摧毁
天阙 Animal对象已经被摧毁
朱砂 Animal对象已经被摧毁
*/
三. 封装
封装: 在面向对象设计中,封装是指数据和实现操作的代码集中起来放在对象内部,并尽可能隐蔽对象的内部细节。对象好像是一个不透明的黑盒子,从外面是看不见的,更不能从外面直接访问或修改这些数据以及代码。我们将对象的特性称为“成员变量”,将对象的行为成为“成员函数”,被封装的特性只能通过特定的行为去访问。
____所谓封装具有两方面的含义:一是将有关的数据和操作代码封装在一个对象中,各个对象相互独立,互不干扰。二是将对象中某些数据与操作代码对外隐蔽,即隐蔽其内部细节,只留下少量接口,以便与外界联系,接收外界消息。
____封装的好处是:将对象的使用者和设计者分开,大大降低了人们操作对象的复杂程度。使用者不必知道对象行为的实现细节,只需要使用者提供的接口即可自如地操作对象。封装的结果实际上隐蔽了复杂性,并提供了代码重用性,从而减轻了开发一个软件系统的难度。