[C++]03-面向对象(3)

1. 面向对象三大特性-继承和封装

      1.1 继承
  • 为什么要有继承:
    • 子类想拥有父类的成员变量和函数
      1.2 封装
  • 什么是封装
    • 狭义上:成员变量属性私有化,对外提供get set方法。即类的成员变量 函数化,自定义函数体,访问安全
    • 广义上:实现特定功能的过程,抽取为函数或类中的方法,需要时候直接调用,避免重复造轮子。
    • 为什么要有封装?
      • 提高代码的复用性,避免重复造轮子
      • 代码访问的安全

2. 多态

      2.1 什么多态?
  • 一个操作用于不同的对象,可以有不同的解释,实现一个函数顶多个函数的作用
  • 通俗点说:在运行时识别真正的对象模型,调用对应子类中的函数。
    在这里插入图片描述
void liu(Animal* p) {
	p->run();
	p->speak();
}
      2.2 实现多态的前提
  • 父类指针可以指向子类对象
      2.3 为什么父类指针可以指向子类对象?
  • 父类指针指向子类对象是不安全。根据指针指向的Person类型,指针变量可访问m_age变量,同时堆空间内存Student对象存在m_age属性。访问m_age时只会修改属于student对象空间的值。不会修改别人空间的值,所以是安全的,允许这样操作。
	Person *p=new Student();
	p->m_age = 30;

在这里插入图片描述

  • 子类指针指向父类对象是不安全。根据指针指向的Student类型,指针变量可访问m_age和m_no变量,但堆空间内存Person对象 只有m_age。访问m_no时会修改不属于person对象空间的值。可能会修改别人空间的数据,所以是不安全的
  • person类型,使用指针可以访问m_age,同时堆空间student对象存在m_age ,所以是安全。
Student* s=(Student*)new Person();
	s->m_age;
	//可能会覆盖掉其他数据 所以存在安全隐患
	s->m_no;

在这里插入图片描述

      2.4 为什么要有多态?
  • 提高代码的扩展性。多态中父类指针指向子类对象,参数传哪个子类对象就调用哪个子类的函数,实现同一份代码有不同的结果,提高代码的扩展性。
    • 例1:以后再需要遛羊,就需要再写一个liu(羊)的函数,代码扩展性很差
    • 例2:java框架中,参数使用父类对象,自定义类继承父类重写父类方法,即可根据自己逻辑对代码进行扩展
struct Dog  {
	// 重写(覆写、覆盖、override)
	void speak() {
		cout << "Dog::speak()" << endl;
	}
	void run() {
		cout << "Dog::run()" << endl;
	}
};
struct Cat  {
	void speak() {
		cout << "Cat::speak()" << endl;
	}
	void run() {
		cout << "Cat::run()" << endl;
	}
};
struct Pig {
	void speak() {
		cout << "Pig::speak()" << endl;
	}
	void run() {
		cout << "Pig::run()" << endl;
	}
};
void liu(Dog* p) {
	p->run();
	p->speak();
}
void liu(Cat* p) {
	p->run();
	p->speak();
}

void liu(Pig* p) {
	p->run();
	p->speak();
}

int main() {
	liu(new Dog());
	liu(new Cat());
	liu(new Pig());
}
  • 怎么办?多态 子类继承父类,使用父类指针指向子类对象,传什么对象 调什么对象的方法,实现一个函数顶多个函数的作用。
struct Pig :public Animal {
	// 重写
	void speak() {
		cout << "Pig::speak()" << endl;
	}
	void run() {
		cout << "Pig::run()" << endl;
	}
};
// liu的函数只需要写一个
void liu(Animal* p) {
	p->run();
	p->speak();
}
int main() {
	liu(new Dog());
	liu(new Cat());
	liu(new Pig());
}
      2.5 多态怎么实现的?
  • c++中的多态通过虚函数(virtual)来实现
  • 只要在父类中声明为虚函数,子类中重写的函数也自动变为虚函数,这样在调用的时候就可以实现多态
struct Animal {
	virtual void speak() {
		cout << "Animal::speak()" << endl;
	}
	virtual void run() {
		cout << "Animal::run()" << endl;
	}
};

struct Dog:public Animal {
	// 重写(覆写、覆盖、override)
	void speak() {
		cout << "Dog::speak()" << endl;
	}
	void run() {
		cout << "Dog::run()" << endl;
	}
};
struct Pig :public Animal {
	// 重写
	void speak() {
		cout << "Pig::speak()" << endl;
	}
	void run() {
		cout << "Pig::run()" << endl;
	}
};

void liu(Animal* p) {
	p->run();
	p->speak();
}
int main() {
	liu(new Dog());
	liu(new Pig());
}
      2.6 虚函数的实现原理?
  • 子类对象空间的前4个字节存储虚表的地址
  • 多态调用对象函数时,根据虚表存储的地址 找到虚表空间
  • 虚表空间中存储虚函数的地址

在这里插入图片描述

struct Animal {
	int age;
	// 虚函数
	virtual void speak() {
		cout << "Animal::speak()" << endl;
	}
	virtual void run() {
		cout << "Animal::run()" << endl;
	}
};

struct Cat :public Animal {
	int life;
	// 重写
	void speak() {
		cout << "Cat::speak()" << endl;
	}
	void run() {
		cout << "Cat::run()" << endl;
	}
};

int main() {
	Animal* p = new Cat();
	p->age = 20;
	p->run();
	p->speak();
}

在这里插入图片描述

  • 汇编分析
    49: 	p->speak();
    // eax=cat对象的地址 0x010105F0
 mov         eax,dword ptr [p]  
 // edx=cat对象前4个地址的值,即虚表的地址0x 00dbab68
 mov         edx,dword ptr [eax]  
 //eax=edx地址的值,即speak函数的地址 
 mov         eax,dword ptr [edx]  
 call        eax  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值