c++之虚函数与多态

一、多态
多态性是指同样的消息被不同类型的对象接收时能导致完全不同的行为。
也可理解为同一个接口,使用不同的实例而执行不同的操作。
多态的实现:函数重载、运算符重载、模板、虚函数

静态绑定:(函数重载、运算符重载 、模板)
绑定过程出现在编译阶段,在编译阶段就已确定要调用的函数。
动态绑定:(虚函数)
绑定过程工作在程序运行时执行,在程序运行时才确定要调用的函数。

二、虚函数
虚函数的概念:在基类中冠以关键字 virtual 的成员函数
虚函数的定义:

virtual 函数类型 函数名称(参数列表);
如果一个函数在基类中被声明为虚函数,则他在所有派生类中都是虚函数
只有通过基类指针或引用调用虚函数才能引发动态绑定,(同时必须在基类中定义相同的接口,接口要定义为虚函数),包括通过基类指针的反引用调用虚函数,因为反引用一个指针将返回所指对象的引用。
虚函数不能声明为静态
(1)在基类中冠以关键字virtual的成员函数。虚函数不能声明为静态。
(2)如果一个函数在基类中被声明为虚函数,则它所有派生类中都是虚函数。
(3)基类的析构函数为虚函数,所有派生类的析构函数都是虚函数。
(4)构造函数不能是虚函数。

构造函数为什么不能是虚函数?
因为每一个拥有虚成员函数的类都有一个指向虚函数表的指针。对象通过虚函数表里存储的虚函数地址来调用虚函数。当我们创建一个对象时,会调用构造函数来构造对象,如果构造函数是虚函数,必须通过虚函数表来找到虚构造函数,然而对象还未创建不存在虚函数表,因此构造函数不能是虚函数。

三、虚函数表指针(vptr)
虚函数的动态绑定是通过虚函数表(在静态数据区)来实现的。(虚函数表存放虚函数的函数指针)
包含虚函数的类对象头4个字节存放指向虚函数表的指针

注意:若不是虚函数,一般的函数不会出现在虚函数表,因为不用通过虚函数表指针间接去访问。

由于vptr在对象中的偏移不会随着派生层次的增加而改变,而且改写的虚函数在派生类vtable中的位置与它在基类vtable中的位置始终保持一致,有了这两条保证,再加上被改写虚函数与其基类中对应虚函数的原型和调用规范都保持一致,自然就能轻松地调用起实际所指对象的虚函数了。

四、虚析构函数
何时需要虚析构函数?
当你可能通过基类指针删除派生类对象时
如果你打算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的),并且被析构的派生类对象是有重要的析构函数需要执行,就需要让基类的析构函数作为虚函数。

为什么基类析构函数要是虚析构函数?

c++基类采用virtual虚析构函数是为了防止内存泄露。如果在派生类中申请内存空间,并在析构函数中空间进行释放,当删除基类指针指向派生类对象时就会触发动态绑定,因而只会调用基类的析构函数。而不会调用派生类的析构函数。

#include <iostream>
using namespace std;


class Base
{
public:
    virtual void Fun1()
    {
        cout << "Base::Fun1 ..." << endl;
    }

    virtual void Fun2()
    {
        cout << "Base::Fun2 ..." << endl;
    }

    void Fun3()
    {
        cout << "Base::Fun3 ..." << endl;
    }

    Base()
    {
        cout << "Base ..." << endl;
    }
    // 如果一个类要做为多态基类,要将析构函数定义成虚函数
    virtual ~Base()
    {
        cout << "~Base ..." << endl;
    }
};

class Derived : public Base
{
public:
    /*virtual */
    void Fun1()
    {
        cout << "Derived::Fun1 ..." << endl;
    }

    /*virtual */void Fun2()
    {
        cout << "Derived::Fun2 ..." << endl;
    }

    void Fun3()
    {
        cout << "Derived::Fun3 ..." << endl;
    }
    Derived()
    {
        cout << "Derived ..." << endl;
    }
    /*  virtual*/ ~Derived() //即使没有virtual修饰,也是虚函数
    {
        cout << "~Derived ..." << endl;
    }
};

int main(void)
{
    Base *p;
    p = new Derived;

    p->Fun1();
    delete p; //通过基类指针删除派生类对象

    return 0;
}

+++++++++++++++++++++++++++++++++++++++
一、纯虚函数
虚函数是实现多态性的前提:

需要在基类中定义共同的接口
接口要定义为虚函数
如果基类的接口没办法实现怎么办?

如形状类Shape
解决方法:

将这些接口定义为纯虚函数

在基类中不能给出有意义的虚函数定义,这时可以把它声明成纯虚函数,把它的定义留给派生类来做
定义纯虚函数:
class 类名{
virtual 返回值类型 函数名(参数表) = 0;
};
纯虚函数不需要实现,纯虚函数不能实例化。

二、抽象类
作用

抽象类为抽象和设计的目的而声明,将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。
对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。
注意

抽象类只能作为基类来使用。
不能声明抽象类的对象。
构造函数不能是虚函数,析构函数可以是虚函数

1、抽象类不能用于直接创建对象实例,可以声明抽象类的指针和引用
2、可使用指向抽象类的指针支持运行时多态性
3、派生类中必须实现基类中的纯虚函数,否则它仍将被看作一个抽象类
4、尽量将基类定义为抽象类

三、RTTI(runtime type information)

RTTI, RunTime Type Information, 运行时类型信息, 是多态的主要组成部分,

(1)dynamic_cast<>的作用
dynamic_cast<>可以检查类的类型(不同的派生类)//包含在头文件#include
dynamic_cast<>可以 使基类指针转换为派生类的指针, 通过判断指针的类型, 可以决定使用的函数.
(2)typeid();
typeid(), 可以判断类型信息, 判断指针指向位置, 在多态中, 可以判断基类还是派生类.
typeid().name();//得到类类型名。

在使用Python来安装geopandas包,由于geopandas依赖于几个其他的Python库(如GDAL, Fiona, Pyproj, Shapely等),因此安装过程可能需要一些额外的步骤。以下是一个基本的安装指南,适用于大多数用户: 使用pip安装 确保Python和pip已安装: 首先,确保你的计算机上已安装了Python和pip。pip是Python的包管理工具,用于安装和管理Python包。 安装依赖库: 由于geopandas依赖于GDAL, Fiona, Pyproj, Shapely等库,你可能需要先安装这些库。通常,你可以通过pip直接安装这些库,但有候可能需要从其他源下载预编译的二进制包(wheel文件),特别是GDAL和Fiona,因为它们可能包含一些系统级的依赖。 bash pip install GDAL Fiona Pyproj Shapely 注意:在某些系统上,直接使用pip安装GDAL和Fiona可能会遇到问题,因为它们需要编译一些C/C++代码。如果遇到问题,你可以考虑使用conda(一个Python包、依赖和环境管理器)来安装这些库,或者从Unofficial Windows Binaries for Python Extension Packages这样的网站下载预编译的wheel文件。 安装geopandas: 在安装了所有依赖库之后,你可以使用pip来安装geopandas。 bash pip install geopandas 使用conda安装 如果你正在使用conda作为你的Python包管理器,那么安装geopandas和它的依赖可能会更简单一些。 创建一个新的conda环境(可选,但推荐): bash conda create -n geoenv python=3.x anaconda conda activate geoenv 其3.x是你希望使用的Python版本。 安装geopandas: 使用conda-forge频道来安装geopandas,因为它提供了许多地理空间相关的包。 bash conda install -c conda-forge geopandas 这条命令会自动安装geopandas及其所有依赖。 注意事项 如果你在安装过程遇到任何问题,比如编译错误或依赖问题,请检查你的Python版本和pip/conda的版本是否是最新的,或者尝试在不同的环境安装。 某些库(如GDAL)可能需要额外的系统级依赖,如地理空间库(如PROJ和GEOS)。这些依赖可能需要单独安装,具体取决于你的操作系统。 如果你在Windows上遇到问题,并且pip安装失败,尝试从Unofficial Windows Binaries for Python Extension Packages网站下载相应的wheel文件,并使用pip进行安装。 脚本示例 虽然你的问题主要是关于如何安装geopandas,但如果你想要一个Python脚本来重命名文件夹下的文件,在原始名字前面加上字符串"geopandas",以下是一个简单的示例: python import os # 指定文件夹路径 folder_path = 'path/to/your/folder' # 遍历文件夹的文件 for filename in os.listdir(folder_path): # 构造原始文件路径 old_file_path = os.path.join(folder_path, filename) # 构造新文件名 new_filename = 'geopandas_' + filename # 构造新文件路径 new_file_path = os.path.join(folder_path, new_filename) # 重命名文件 os.rename(old_file_path, new_file_path) print(f'Renamed "{filename}" to "{new_filename}"') 请确保将'path/to/your/folder'替换为你想要重命名文件的实际文件夹路径。
在使用Python来安装geopandas包,由于geopandas依赖于几个其他的Python库(如GDAL, Fiona, Pyproj, Shapely等),因此安装过程可能需要一些额外的步骤。以下是一个基本的安装指南,适用于大多数用户: 使用pip安装 确保Python和pip已安装: 首先,确保你的计算机上已安装了Python和pip。pip是Python的包管理工具,用于安装和管理Python包。 安装依赖库: 由于geopandas依赖于GDAL, Fiona, Pyproj, Shapely等库,你可能需要先安装这些库。通常,你可以通过pip直接安装这些库,但有候可能需要从其他源下载预编译的二进制包(wheel文件),特别是GDAL和Fiona,因为它们可能包含一些系统级的依赖。 bash pip install GDAL Fiona Pyproj Shapely 注意:在某些系统上,直接使用pip安装GDAL和Fiona可能会遇到问题,因为它们需要编译一些C/C++代码。如果遇到问题,你可以考虑使用conda(一个Python包、依赖和环境管理器)来安装这些库,或者从Unofficial Windows Binaries for Python Extension Packages这样的网站下载预编译的wheel文件。 安装geopandas: 在安装了所有依赖库之后,你可以使用pip来安装geopandas。 bash pip install geopandas 使用conda安装 如果你正在使用conda作为你的Python包管理器,那么安装geopandas和它的依赖可能会更简单一些。 创建一个新的conda环境(可选,但推荐): bash conda create -n geoenv python=3.x anaconda conda activate geoenv 其3.x是你希望使用的Python版本。 安装geopandas: 使用conda-forge频道来安装geopandas,因为它提供了许多地理空间相关的包。 bash conda install -c conda-forge geopandas 这条命令会自动安装geopandas及其所有依赖。 注意事项 如果你在安装过程遇到任何问题,比如编译错误或依赖问题,请检查你的Python版本和pip/conda的版本是否是最新的,或者尝试在不同的环境安装。 某些库(如GDAL)可能需要额外的系统级依赖,如地理空间库(如PROJ和GEOS)。这些依赖可能需要单独安装,具体取决于你的操作系统。 如果你在Windows上遇到问题,并且pip安装失败,尝试从Unofficial Windows Binaries for Python Extension Packages网站下载相应的wheel文件,并使用pip进行安装。 脚本示例 虽然你的问题主要是关于如何安装geopandas,但如果你想要一个Python脚本来重命名文件夹下的文件,在原始名字前面加上字符串"geopandas",以下是一个简单的示例: python import os # 指定文件夹路径 folder_path = 'path/to/your/folder' # 遍历文件夹的文件 for filename in os.listdir(folder_path): # 构造原始文件路径 old_file_path = os.path.join(folder_path, filename) # 构造新文件名 new_filename = 'geopandas_' + filename # 构造新文件路径 new_file_path = os.path.join(folder_path, new_filename) # 重命名文件 os.rename(old_file_path, new_file_path) print(f'Renamed "{filename}" to "{new_filename}"') 请确保将'path/to/your/folder'替换为你想要重命名文件的实际文件夹路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值