小甲鱼-C++ 23 虚方法

本文介绍了C++中的动态内存管理关键字new和delete,并通过一个实例展示了在没有声明虚函数时,基类指针调用子类方法的问题。通过添加virtual关键字使play()成为虚函数,解决了运行时的多态问题。
摘要由CSDN通过智能技术生成

写在前面:作为一只小白,感谢小甲鱼老师提供这么好的入门课程。因此在这里做个笔记,如有侵权请联系删除

www.fishc.com

https://blog.csdn.net/qq_30708445/article/details/88596720

先来介绍两个c++保留字:new和delete

在c或者C++中完全可以在没有创建变量的情况下为有关数据分配内存。也就是直接创建一个指针并让它指向新分配的内存块:

 
  1. int *pointer = new int;

  2. *pointer = 110;

  3. cout << *pointer;

  4. delete pointer;

new和malloc没什么区别,new只是malloc的进化版本,进行了封装

注意:最后一步非常必要和关键,因为程序不会自动释放内存,程序中的每一个new操作都必须有一个与之对应的delete操作

例子:

#include <iostream>
#include <string>
 
using namespace std;
 
class Pet
{
public:
	Pet(string theName);
 
	void eat();
	void sleep();
	void play();
 
protected:
	string name;
};
 
class Cat:public Pet
{
public:
	Cat(string theName);
 
	void climb();
	void play();
};
 
class Dog:public Pet
{
public:
	Dog(string theName);
 
	void bark();
	void play();
};
 
Pet::Pet(string theName)
{
	name = theName;
}
 
void Pet::eat()
{
	cout << name << "正在吃!" << endl;
}
 
void Pet::sleep()
{
	cout << name << "正在睡大觉!" << endl;
}
 
void Pet::play()
{
	cout << name << "正在玩儿!" << endl;
}
 
Cat::Cat(string theName):Pet(theName)
{
}
 
void Cat::climb()
{
	cout << name << "正在爬树!" << endl;
}
 
void Cat::play()
{
	Pet::play();     //对基类中的方法进行覆盖
	cout << name << "玩毛线球!" << endl;
}
 
Dog::Dog(string theName):Pet(theName)
{
}
 
void Dog::bark()
{
	cout << name << "旺旺!" << endl;
}
 
void Dog::play()
{
	Pet::play();   //对基类中的方法进行覆盖
	cout << name << "正在追赶那只该死的猫!" << endl;
}
 
int main()
{
	Pet *cat = new Cat("加菲");
	Pet *dog = new Dog("欧迪");
 
	cat -> sleep();
	cat -> eat();
	cat -> play();
 
	dog -> sleep();
	dog -> eat();
	dog -> play();
	
	delete cat;
	delete dog;
 
 
	return 0;
}

运行结果

问题:

通过输出结果可以发现,程序与预期的结果不符:我们在Cat和Dog类里对play()方法进行了覆盖,但实际上调用的是Pet::play()方法而不是那两个覆盖的版本。

原因:

在程序编译的时候,编译器将检查所有的代码,在如何对某个数据进行处理和可以对该类型的数据进行何种处理之间寻找到一个最佳点。正是这一项编译时的检查影响了刚才的程序结果:cat和dog在编译时都是Pet类型指针,编译器就认为两个指针调用的paly()方法是Pet::play()方法,因为这是执行起来最快的解决方案。而引发问题的源头就是我们使用了new在程序运行的时候才为dog和cat分配Dog类型和Cat类型的指针。这些是它们在运行时才分配的类型,和它们在编译时的类型是不一样的。为了让编译器知道它应该根据这两个指针在运行时的类型而有选择地调用正确的方法(Dog::play()和Cat::play()),我们必须把这些方法声明为虚方法。

声明虚方法:

virtual void play();

虚方法是继承的,一旦在基类里把某个方法声明为虚方法,在子类里就不可能再把它声明为一个非虚方法了。

因此只需要在Pet类的play()方法前面加上virtual即可

#include <iostream>
#include <string>
 
using namespace std;
 
class Pet
{
public:
	Pet(string theName);
 
	void eat();
	void sleep();
	virtual void play();
 
protected:
	string name;
};
 
class Cat:public Pet
{
public:
	Cat(string theName);
 
	void climb();
	void play();
};
 
class Dog:public Pet
{
public:
	Dog(string theName);
 
	void bark();
	void play();
};
 
Pet::Pet(string theName)
{
	name = theName;
}
 
void Pet::eat()
{
	cout << name << "正在吃!" << endl;
}
 
void Pet::sleep()
{
	cout << name << "正在睡大觉!" << endl;
}
 
void Pet::play()
{
	cout << name << "正在玩儿!" << endl;
}
 
Cat::Cat(string theName):Pet(theName)
{
}
 
void Cat::climb()
{
	cout << name << "正在爬树!" << endl;
}
 
void Cat::play()
{
	Pet::play();     //对基类中的方法进行覆盖
	cout << name << "玩毛线球!" << endl;
}
 
Dog::Dog(string theName):Pet(theName)
{
}
 
void Dog::bark()
{
	cout << name << "旺旺!" << endl;
}
 
void Dog::play()
{
	Pet::play();   //对基类中的方法进行覆盖
	cout << name << "正在追赶那只该死的猫!" << endl;
}
 
int main()
{
	Pet *cat = new Cat("加菲");
	Pet *dog = new Dog("欧迪");
 
	cat -> sleep();
	cat -> eat();
	cat -> play();
 
	dog -> sleep();
	dog -> eat();
	dog -> play();
	
	delete cat;
	delete dog;
 
 
	return 0;
}

运行结果:

加菲正在睡大觉!
加菲正在吃!
加菲正在玩儿!
加菲玩毛线球!
欧迪正在睡大觉!
欧迪正在吃!
欧迪正在玩儿!
欧迪正在追赶那只该死的猫!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值