c++学习笔记:多态

4月1日

一、多态

1、多态分为两类:静态多态、动态多态

静态多态:函数的重载和运算符的重载属于静态多态,复用函数名

动态多态:派生类和虚函数运行时实现多态

区别:

静态多态的函数地址早绑定,编译阶段确定函数地址

动态多态的函数地址晚绑定,运行阶段确定函数地址

2、函数重写:函数返回值类型、函数名、参数列表完全相同

3、动态多态满足条件:

1)有继承关系

2)子类重写父类的虚函数

4、动态多态的使用

父类的指针或者引用指向子类的对象

5、多态案例—计算机类

1)多态的优点

代码组织清晰

可读性强

利于后期的扩展以及维护

2)案例

传统写法:

传统写法:如果想扩展新功能,需要修改源码,在真实的开发中,提倡开闭原则:对扩展进行开发,对修改进行关闭

多态写法:

代码:

#include<iostream>

using namespace std;


//利用多态实现计算器

//实现计算器抽象类

class AbstractCalculator{

public:

    int m_num1;

    int m_num2;

    //设为虚函数

    virtual int getResult()

    {

        return 0;

    }

};



//加法计算器类

class AddCalculator:public AbstractCalculator {

public:

    int getResult()

    {

        return AbstractCalculator::m_num1 +AbstractCalculator::m_num2;

    }

    int m_num1; //若此处再声明成员变量,则会造成此类的成员将父类的覆盖掉相加,而不能实现多态,或者加作用域也可以也可以

    int m_num2;

};



//减法计算器类

class SubCalculator :public AbstractCalculator {

public:

    int getResult()

    {

        return m_num1 - m_num2;

    }



};

//乘法计算器类

class MulCalculator :public AbstractCalculator {

public:

    int getResult()

    {

        return m_num1 * m_num2;

    }

   

};

//除法计算器类

class ChuCalculator :public AbstractCalculator {

public:

    int getResult()

    {

        return m_num1 / m_num2;

    }

   

};



void test02()

{

    //多态使用方法:父类的指针或引用指向子类对象

    AbstractCalculator *abc = new AddCalculator;

    abc->m_num1 = 10;

    abc->m_num2 = 5;

    cout << abc->m_num1 << "+" << abc->m_num2 << "=" << abc->getResult() << endl; //注意子类中不能再重新声明m_num1,m_num2,否则会造成出错

    //对象用完后,记得销毁,因为其置于堆区:需手动销毁

    delete abc;

    abc = new SubCalculator;

    abc->m_num1 = 20;

    abc->m_num2 = 15;

    cout << abc->m_num1 << "-" << abc->m_num2 << "=" << abc->getResult() << endl;

    delete abc;



    abc = new MulCalculator;

    abc->m_num1 = 10;

    abc->m_num2 = 15;

    cout << abc->m_num1 << "*" << abc->m_num2 << "=" << abc->getResult() << endl;

    delete abc;

    abc = new ChuCalculator;

    abc->m_num1 = 200;

    abc->m_num2 = 2;

    cout << abc->m_num1 << "/" << abc->m_num2 << "=" << abc->getResult() << endl;

    delete abc;



}


int main()

{

    test02();

    return 0;

}

6、纯虚函数和抽象类

在多态中,通常父类中的虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将其改为纯虚函数。

1)语法 virtual 返回值类型 函数名(参数列表)=0;

当类中有了纯虚函数,这个类也称为抽象类

2)抽象类的特点:

无法实例化对象;子类必须重写抽象类中的纯虚函数,否则也属于抽象类;

7、虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决方法:将父类中的析构函数改为虚析构和纯虚析构

1)虚析构和纯虚析构的共性:

可以解决父类指针释放子类对象、都需要具体的函数实现

2)区别

如果是纯虚析构,该类属于抽象类,无法实例化对象

3)示例

常规不写虚析构的写法

#include<iostream>

using namespace std;

#include<string>



class Drink{

public:

    int m_num1;

    int m_num2;

    Drink()

    {

        cout << "Drink的构造函数调用" << endl;

    }

    //纯虚函数 此时类为抽象类

    virtual void zhuWater() = 0;

    void makeDrink()

    {

        zhuWater();

    }

    ~Drink()

    {

        cout << "Drink的析构函数调用" << endl;

    }

};



//子类

class Tea:public Drink {

public:

    Tea(string name)

    {

        cout << "Tea构造函数" << endl;

        m_Name = new string(name);

    }

    virtual void zhuWater()

    {

        cout<<*m_Name << "煮水" << endl; //注意指针加*解引用

    }

    ~Tea()

    {

        if (m_Name != NULL)

        {

             cout << "Tea的析构函数调用" << endl;

             delete m_Name;

             m_Name = NULL;

        }

    }

    string *m_Name;

   

};





void test02()

{

    Drink *a = new Tea("龙井");

    a->makeDrink();

    delete a;

}



int main()

{

    test02();

    return 0;

}

结果:

可以看出并没有调用子类的析构来销毁在堆区建的属性数据。堆区数据没有释放干净会导致内存泄漏。

用虚析构解决:

纯虚析构(需要声明又需要有实现):

有了纯虚析构之后这个类也属于抽象类,无法实例化;

不是每个类都需要写虚析构或者纯虚析构,它们都是为了解决多态时,在子类堆区创建了数据,父类无法调用子类析构代码来释放堆区数据造成内存泄漏的问题。若子类堆区无数据,则可以不写它们。

8、案例:电脑组装

一台电脑分为CPU、内存条、显卡 分别写为三个抽象基类 并提供其功能的纯虚函数作为后续调用接口;

然后写不同的厂商类(生产不同的CPU、显卡、内存条),并实现其纯虚函数;

然后创建一个电脑类提供让电脑工作的函数,并且调用每个零件工作的接口;

即用不同厂商的零件去组装它,并使它工作;

代码:

#include<iostream>
#include<string>
using namespace std;
//cpu类
class CPU {
//注意若不写明其访问权限 则默认为私有,类外不可访问
public:
	//纯虚函数 计算
	virtual void calculate() = 0;
};

//显卡类
class VideoCard {
public:
	//纯虚函数 显示
	virtual void display() = 0;
};

//内存条类
class StoreTiao {
public:
	//纯虚函数 存储
	virtual void store() = 0;
};

//电脑类 提供工作的函数
class Computer {
public:
	//构造函数 传入详细零件指针
	Computer(CPU *cpu, VideoCard *videoCard, StoreTiao *memory)
	{
		m_cpu = cpu;
		m_videoCard = videoCard;
		m_memory = memory;
	}

	//让零件工作起来 调用其接口
	void doWork()
	{
		m_cpu->calculate();
		m_videoCard->display();
		m_memory->store();
	}
	//提供电脑析构函数 释放3个电脑零件
	~Computer()
	{
		if (m_cpu != NULL)
		{
			delete m_cpu;
			m_cpu = NULL;
		}
		if (m_videoCard != NULL)
		{
			delete m_videoCard;
			m_videoCard = NULL;
		}
		if (m_memory != NULL)
		{
			delete m_memory;
			m_memory = NULL;
		}
		cout << "析构函数将零件内存释放完毕" << endl;
	}
private:
	CPU *m_cpu;
	VideoCard *m_videoCard;
	StoreTiao *m_memory;
};

//具体厂商 
//戴尔
class DelMemory : public StoreTiao {
public:
	virtual void store()
	{
		cout << "戴尔内存条开始工作 存储" << endl;
	}
};

class DelCPU :public CPU {
public:
	virtual void calculate()
	{
		cout << "戴尔CPU开始工作 计算" << endl;
	}
};

class DelVideoCard : public VideoCard{
public:
	virtual void display()
	{
		cout << "戴尔显卡开始工作 显示" << endl;
	}

};

//Lenovo
class LenovoMemory : public StoreTiao {
public:
	virtual void store()
	{
		cout << "联想内存条开始工作 存储" << endl;
	}
};

class LenovoCPU :public CPU {
public:
	virtual void calculate()
	{
		cout << "联想CPU开始工作 计算" << endl;
	}
};

class LenovoVideoCard : public VideoCard {
public:
	virtual void display()
	{
		cout << "联想显卡开始工作 显示" << endl;
	}
};


//Intel
class IntelMemory : public StoreTiao {
public:
	virtual void store()
	{
		cout << "因特尔内存条开始工作 存储" << endl;
	}
};

class IntelCPU :public CPU {
public:
	virtual void calculate()
	{
		cout << "因特尔CPU开始工作 计算" << endl;
	}
};

class IntelVideoCard : public VideoCard {
public:
	virtual void display()
	{
		cout << "因特尔显卡开始工作 显示" << endl;
	}

};

void test01()
{
	//第一台电脑零件 
	//数据存放在堆区 需手动释放
	CPU *intelCpu = new IntelCPU;
	VideoCard *delVideoCard = new DelVideoCard;
	StoreTiao *lenovoMemore = new LenovoMemory;
	//创建第一台电脑
	//Computer computer1(intelCpu, delVideoCard, lenovoMemore);//第一种创建 放在栈区
	Computer *computer1 = new Computer(intelCpu, delVideoCard, lenovoMemore);//第二种创建 放在堆区 需手动释放
	computer1->doWork();
	delete computer1;
}

int main()
{
	test01();
	return 0;
}

注意释放内存!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 多态是面向对象编程中的一个重要概念,它指的是同一个方法或者同一个类在不同的情况下表现出不同的行为。在实际编程中,我们可以通过继承、接口等方式来实现多态。通过多态,我们可以提高代码的可复用性和可扩展性,使得程序更加灵活和易于维护。在使用多态时,我们需要注意一些细节,比如要遵循里氏替换原则,保证子类可以替换父类,不破坏程序的正确性。 ### 回答2: 多态,是面向对象编程中一个非常重要的概念,也是面向对象编程的三大特性之一,同封装和继承。它允许不同的对象对同一个消息作出不同的响应,也就是说,多态性指的是同一操作符或方法作用于不同的对象时,会产生不同的结果。在实际开发中,多态性可以使我们在编写程序时更加灵活、方便和高效。 多态性的实现,通常是通过继承和接口来实现的。当一个类实现了一个接口时,该类可以被看作是该接口的实例,并且可以被统一的调用。而在实际的应用中,多态常常会被用于设计模式中的策略模式、工厂模式等等。 在阿猫阿狗的第3关中,我们需要使用多态来实现不同种类的动物之间的交互。在这个例子中,我们使用了一个抽象的动物类 Animal 去定义所有动物的公共属性和方法,然后针对不同的种类动物,我们分别定义了不同的子类。例如,猫和狗是不同的子类,它们继承了 Animal 类,然后根据自己的需要进行了方法的重写,使得它们能够根据不同的动物状态进行不同的动作。 在游戏中,玩家需要根据游戏的要求,选择不同种类的动物进行操作。因为所有动物都继承了 Animal 类,所以我们可以通过 Animal 类的实例来操作不同种类的动物,而且操作的方式和方法都是一样的,这就是多态性的实现。 总之,多态性作为面向对象编程的重要特性之一,可以极大的提高程序的灵活性和可扩展性。在实际开发中,我们可以将多态性应用于更加复杂的系统设计中,从而使我们的程序设计更加高效、合理和可靠。 ### 回答3: 多态是面向对象编程的核心之一,它体现了面向对象编程的一个重要特征:重载。以“阿猫阿狗”为例,猫和狗都是宠物,它们有相同的属性,例如名字、年龄和品种等,但猫和狗又具有不同的行为,比如狗会汪汪叫,猫会喵喵叫。这就是多态多态的实现,需要利用面向对象的继承、封装和重写机制。 在“阿猫阿狗”这个例子中,我们可以定义一个Pet类,表示所有宠物都需要具备的属性和行为。然后,定义猫和狗的类,它们都继承自Pet类。在Pet类中,我们定义一个抽象的叫声方法,让猫和狗各自去重写这个方法,在具体的实现中,狗会输出“汪汪”,而猫会输出“喵喵”。 这样,我们就可以实现对猫和狗的多态操作了。在程序中,我们可以定义一个Pet类型的引用变量,它可以指向一个猫或者一个狗对象。当我们调用叫声方法时,具体执行哪个方法是由实际的对象来决定的。例如,如果Pet变量指向一个狗对象,调用叫声方法时就会输出“汪汪”。 多态的使用让程序变得更加灵活和可扩展,我们可以通过它来管理不同种类的对象,简化代码结构,提高代码的重用性和可维护性。在实际开发中,多态也是非常常用的,比如在Java中,很多核心API都支持多态调用,例如List、Set和Map等容器类,可以存储和操作不同类型的对象。 总之,多态是面向对象编程中非常重要的概念,实现多态需要继承、封装和重写技术的支持,它使得程序更加灵活和可扩展,提高了程序的重用性和可维护性。在实际开发中,尤其是大型项目中,多态的应用更是不可或缺的。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值