《深度探索C++对象模型》读书笔记(1)仅含前四章

1.C++对虚拟基类的支持会导致一点额外的负担,具体实现是在派生类对象中使用一个指针来指向对应的虚拟基类,这个指针指向一个表格,表格存放的是引导访问虚拟基类子对象的信息,例如其在派生类对象中的偏移量或者内存地址,这取决于编译器实现。

 

2.没有任何数据成员的类,为了在内存中取得唯一的内存地址,所以会由编译器插入一个空的byte。但是这个优化仅仅是在没有任何数据成员的类身上实现。(VS2017中这个优化已经不存在了)

 

3.typedef得到的别名应该写到所有使用到这个别名的类成员声明的前面,这样可以避免发生跟类外的重名发生。

 

4.


 

5.静态数据成员被引用在编译器中会被直接替换为对data segement的数据的直接引用,一般不经过类对象引用。但是在C++语法上存在一个可能,那就是通过函数返回值引用静态数据成员,这个时候单纯地直接替换就会造成函数在实际上并没有执行。对于这个情况的处理一般来说就分拆开两部,先调用函数,然后再直接引用。


 

6.对静态数据成员取指针得到的不是类成员的指针,而是该成员的数据类型的指针。ps:类成员指针其实是其类内偏移值

 

7.由于对非静态数据成员进行存取是通过类对象地址+偏移量来寻址的,而偏移量是编译器就可以计算获得的,所以存取一个非静态数据成员效率跟普通地去存取C风格的结构体数据是一样的,即使这个非静态数据成员来自一个只包含普通基类的继承链。(继承链中包含虚拟基类情况下这个结论不成立)

 

8.C++通过在类对象中插入vptr来支持多态,这个vptr指向一个vtable,当派生类对象被创建的时候,在构造函数里面会根据实际情况重写vtable的内容和vptr数据。

 

9.单一继承由于结构简单,一般内存布局上基类子对象放在派生类对象的开头,所以两者的地址应该是一样,这是天然的多态,只需要对该指针进行不同的解析就好。多重继承由于内部存在多个不同的基类子对象,自然基类子对象的地址也是不一样的,要对这样的派生类执行多态,一般是编译器把相关的基类子对象指针操作内在地使用头地址+offset的方式进行。

 

10.虚拟继承一般的实现方法:把含有虚拟基类的类数据分成不变局部和共享局部,不变局部一般是指非虚拟基类数据成员的部分,为了实现共享局部的共享,一般就是间接存取,以保证不同情况的虚拟继承能有相同的布局。

 

11.虚拟继承为了实现共享局部的共享,一般都是在类内部保存一份共享局部的数据,在各个派生类对象内部持有一份访问该共享局部数据的指针。但是当继承链中有超过一个虚拟基类的话,那么需要持有的指针也需要相应地增加。为了解决这个问题,一般有两种常用的策略,一种是需要再增加一层间接存取,建立一个virtual base class table,类同于virtual function table,这样的话无论虚拟基类有多少个,只需要增加virtual base class table的长度就可以了,派生类持有的virtualbase class table指针就只要一个。第二种是把这个virtual base class table建在virtual function table的负向坐标。

上面所做的一切努力都是为了让虚拟继承的内存布局能稳定一些,不会因为继承链过于复杂而复杂程度大幅增加。由于多态只发生在指针或者引用出现的场合,所以一般的类对象存取虚拟基类成员的时候,不需要这么复杂的间接寻址,可以直接在编译期就计算出成员地址。

 

12.对类数据对象取指针得到的是该对象在类内部的偏移值,一般来说这个偏移值会比实际情况大1,这是为了区分指向第一个数据成员的指针和空指针0

 

13.静态成员函数没有this指针,它的出现是为了解决存取静态成员变量的封装问题,具体例子就是单例模式.

对静态成员函数取址得到的是实际的内存地址。

Class A{

Public:

StaticA&Instance()

{

If(_instance == 0)

{

_instance = new A();

}

 

Return *_instance;

}

 

Private:

Static A*_instance;

A();

};

 

14.单一继承对于虚函数的处理比较简单,由于只有一个单一的基类,在编译期就可以把派生类中的vptr在类中的地址确定好。所以相对比较简单。


 

15.多重继承在支持虚函数上主要面临的问题是如果通过指向非第一基类的指针调用虚函数,这个时候必须要在每一个非第一基类内部保存一份vptr,这个vptr指向的vtable与第一基类的vptr指向的vtable的内容有所不同,这是因为这个时候需要动态调整this指针来保证在调用派生类的函数的时候不会使用了非第一基类的this,应该使用派生类的this。非第一基类的vtable要另外保存offset信息来计算出派生类的真正地址。


 

16.虚拟继承跟单一继承的区别在于虚基类的子对象在派生类对象中只能通过vtable的一个offset值来存取,这样有助于统一派生类与其他类的内存布局的一致性。


 

17.一个指向非静态非虚拟成员函数的指针,需要绑定于某个类对象的地址上才能被正确地执行,因为调用中隐含地传入类对象地址作为this指针。由于静态成员函数不需要this指针,所以对应的指针类型应该是一般的“函数指针”,而不是“指向成员函数的指针”。

 

18.对虚函数取址得到的是该函数地址在vtable里面的索引值。这里有个问题,如果类里面有两个函数原型一样仅仅是函数名不一样的函数,一个是虚函数,一个是普通的成员函数,那么在执行ptr->*pmf的时候怎么确定是调用普通的成员函数还是虚函数?对于这个问题各个编译器的处理方法不一样

 

19.由于inline函数需要在调用点原地展开代码,所以有可能增加源程序的大小。

 

20.避免滥用虚拟函数机制,对于在派生类中极小可能被重写的函数,尽量不声明为虚函数,这样会增加调用成本。另外虚函数尽量避免声明为const,因为这样会限制派生类中的函数修改数据成员的能力。最后就是避免把基类的析构函数声明为纯虚函数,一般声明为虚函数。

在使用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、付费专栏及课程。

余额充值