多态学习总结

本文深入探讨了C#中的多态概念及其通过重写实现的方式。介绍了子类如何通过重写父类方法来满足特定需求,以及使用base关键字访问父类成员的重要性。同时,文章讲解了里氏转换原则,并说明了多态如何提高程序的灵活性、可扩展性和可维护性。

 

多态就是指同一操作作用于不同的对象会产生不同的结果。在继承关系中,子类会自动继承父类中的方法,但有时父类的方法不能满足子类的需求,可以对父类的方法进行重写。重写有三点要求,1 要求子类的方法名,参数个数,参数类型必须与父类方法相同,2父类方法必须使用们virtual关键字修饰,被它修饰的方法称为虚方法,3 子类方法必须使用override的关键字修饰。需要注意的是,子类重写父类方法时,不能使用比父类中被重写的方法更严格的访问权限。

当子类重写父类的方法后,子类对象将无法直接访问父类被重写的方法,这里就可以使用base关键字,专门用于在子类中访问父类的成员.它可以调用父类的子段和方法。base.字段名,base.方法名。还能调用父类的构造方法。:base( )。

实现多态的方式有很多种,这里学习的是重写的方式。使用一个父类类型的变量来引用一个子类类型的对象,例如Aninal an1=new Cat(),创建cat子类对象,使用animal父类类型的变量an1引用。将子类对象当了父类类型来使用,这就是所谓的里氏转换原则。1 子类对象可以直接赋值给父类变量,例如Sninal an1=new Cat( ),这就是将子类对象当做父类类型使用。2 将父类变量转换为子类类型(父类变量引用的是当前的子类对象)。

在c#中提供了一个object类,它是所有类的父类。它有个tostring()方法。可以把它重写来返回一些特有的信息。

多态可以使程序变得更加灵活,从而有效提高程序的可扩展性和可维护性。

转载于:https://www.cnblogs.com/hzusbjxg/p/10705261.html

<think>我们正在讨论C++的多态特性。根据引用内容,多态的实现原理主要涉及虚函数和虚函数表(vtable)。使用方法是在基类函数前加virtual关键字,在派生类中重写该函数,通过基类指针或引用调用会根据实际对象类型调用相应函数。 首先,我们需要明确多态分为静态多态(编译多态,如函数重载)和动态多态(运行多态,即虚函数机制)。用户问题主要关注动态多态。 原理部分: 1. 当类中有虚函数,编译器会为该类生成一个虚函数表(vtable),表中存放虚函数的函数指针。 2. 每个对象在创建,会在其内存布局的开头(通常)加入一个指向虚函数表的指针(vptr)。 3. 当通过基类指针或引用调用虚函数,程序会根据vptr找到虚函数表,然后根据函数在表中的偏移量找到实际要调用的函数地址。 使用方法: 1. 在基类中声明虚函数(使用virtual关键字)。 2. 在派生类中重写(override)该虚函数(函数签名相同)。 3. 通过基类指针或引用来指向派生类对象,并调用虚函数,此会调用派生类中的函数。 注意:构造函数不能是虚函数,而析构函数通常声明为虚函数(特别是基类),以确保正确释放派生类资源。 下面我们用代码示例来说明: 首先,定义一个基类Animal,其中有一个虚函数speak: ```cpp #include <iostream> using namespace std; class Animal { public: virtual void speak() { cout << "Animal speaks." << endl; } // 虚析构函数,确保正确释放资源 virtual ~Animal() {} }; class Dog : public Animal { public: void speak() override { // override关键字(C++11)表示重写,可加可不加,但推荐使用 cout << "Dog barks." << endl; } }; class Cat : public Animal { public: void speak() override { cout << "Cat meows." << endl; } }; int main() { Animal* animal1 = new Dog(); Animal* animal2 = new Cat(); animal1->speak(); // 输出:Dog barks. animal2->speak(); // 输出:Cat meows. delete animal1; delete animal2; return 0; } ``` 在上面的例子中,Animal类中的speak函数被声明为虚函数。当我们通过基类指针(Animal*)调用speak,实际调用的是指针所指向的对象的实际类型(Dog或Cat)中的speak函数。 关于虚函数表(vtable)和虚指针(vptr): 每个有虚函数的类(或从有虚函数的类派生)都有一个虚函数表,该表在编译创建。每个对象在构造,其虚指针被初始指向相应的虚函数表。当调用虚函数,通过虚指针找到虚函数表,再通过偏移量找到正确的函数地址。 注意:多态只能通过指针或引用来实现,直接使用对象不会发生多态(因为对象类型在编译确定)。 另外,引用[4]提到MFC库使用消息映射机制而不是虚函数,这是为了避免每个类都拥有虚表带来的空间开销。但在一般情况下,虚函数机制是C++实现多态的标准方式。 最后,我们总结一下: 1. 动态多态通过虚函数实现,在运行根据对象类型确定调用的函数。 2. 虚函数表是实现动态多态的关键,每个类一个,存放虚函数地址。 3. 每个对象有一个指向虚函数表的指针(vptr)。 4. 使用基类指针或引用调用虚函数,通过vptr找到实际类的虚函数表,从而调用正确的函数。 相关问题: 1. C++中虚函数表是如何创建的? 2. 为什么构造函数不能是虚函数? 3. 虚析构函数的作用是什么? 4. 多态在什么情况下不会发生? 5. C++11中的override和final关键字有什么作用?</think>### C++多态的实现原理及使用方法 #### 一、多态的实现原理 C++多态的核心机制是**虚函数表(vtable)** 和 **虚表指针(vptr)**,其工作原理如下: 1. **虚函数表(vtable)** 当类中包含虚函数,编译器会为该类生成一个虚函数表: - 表中存储该类所有虚函数的入口地址 - 每个有虚函数的类拥有独立的虚函数表 - 示例:`Animal` 类的虚函数表包含 `virtual void speak()` 的地址 2. **虚表指针(vptr)** 每个对象在创建: - 编译器隐式添加一个指向vtable的指针(vptr) - vptr通常位于对象内存布局的首部 - 大小通常为4字节(32位系统)或8字节(64位系统) 3. **动态绑定过程** 当通过基类指针/引用调用虚函数: ```cpp Animal* animal = new Dog(); animal->speak(); // 动态绑定 ``` 执行流程: - 通过对象的vptr找到对应的虚函数表 - 在虚函数表中查找函数偏移位置 - 调用实际指向的派生类函数[^3] $$ \text{调用过程} : \text{对象} \xrightarrow{\text{vptr}} \text{vtable} \xrightarrow{\text{偏移量}} \text{实际函数地址} $$ #### 二、多态使用方法 实现多态需要三个关键步骤: 1. **基类声明虚函数** 使用 `virtual` 关键字声明接口函数: ```cpp class Animal { public: virtual void speak() { // 虚函数声明 cout << "Animal sound" << endl; } virtual ~Animal() {} // 虚析构函数(必需!) }; ``` 2. **派生类重写虚函数** 派生类使用相同签名覆盖函数: ```cpp class Dog : public Animal { public: void speak() override { // C++11推荐使用override cout << "Woof!" << endl; } }; ``` 3. **通过基类指针/引用调用** 运行根据实际对象类型决定调用: ```cpp int main() { Animal* animals[2]; animals[0] = new Dog(); // 派生类对象 animals[1] = new Cat(); // 另一个派生类 animals[0]->speak(); // 输出"Woof!" (动态绑定) animals[1]->speak(); // 输出"Meow!" // 必须通过基类指针删除对象 delete animals[0]; delete animals[1]; } ``` #### 三、关键注意事项 1. **虚析构函数** $$ \text{基类析构函数必须声明为virtual} $$ 避免派生类资源泄漏: ```cpp Base* obj = new Derived(); delete obj; // 若基类析构非虚,仅调用~Base() ``` 2. **override关键字(C++11)** 显式标记重写,避免隐藏错误: ```cpp class Derived : public Base { void foo() override; // 编译检查是否成功重写 }; ``` 3. **final关键字(C++11)** 禁止进一步重写: ```cpp class Base { virtual void foo() final; // 禁止派生类重写 }; ``` 4. **纯虚函数与抽象类** 定义接口规范: ```cpp class Shape { // 抽象类 public: virtual double area() = 0; // 纯虚函数 }; ``` #### 四、多态类型对比 | 类型 | 实现方式 | 绑定机 | 示例 | |------------|---------------|----------|--------------------| | 动态多态 | 虚函数 | 运行 | `virtual void f()` | | 静态多态 | 函数重载/模板 | 编译 | `template <typename T>` | > ⚠️ **重要限制**:构造函数不能是虚函数,但虚构造函数可通过工厂模式模拟[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值