二、转换的本质 派生类可以转换为基类的本质是: ①为什么派生类可以转换为基类:派生类从基类而来,因此派生类中包含了基类的方法和成员。...此时基类可以通过指针或引用指向派生类(相当于将派生类从基类中继承的那部分方法和成员绑定到基类上了,相当于派生类被截断了),然后基类就可以将派生类假装是一个基类对象来使用(调用其中的成员/方法) ②为什么基类不能转换为派生类...如果将一个基类对象绑定到派生类的指针/引用上,此时派生类通过指针/引用访问自己新定义的成员/方法时,发现找不到(因此不能将基类转换为派生类) 例如:下面B继承于A,子类继承于父类,同时为父类的成员开辟了空间...三、继承方式对类型转换的影响 遵循下面3个规则: 假设B继承于A ①只有当B公有地继承A时,用户代码才能使用派生类向基类转换;如果B是受保护的/私有的继承于A,则不能使用派生类向基类转换 因为保护或者私有继承...,因为其只能与自己类型一致的对象绑定到一起 演示案例 当我们使用基类的引用(或指针)时,我们并不清楚该引用(或指针)所绑定的对象的真实类型,该对象可能是基类的对象,也可能是派生类的对象。
class SomeClass: private Uncopyable{ ... }; ---- 条款7、为多态基类声明virtual析构函数 C++中多态性质体现于虚函数:基类指针或引用调用虚函数时会检查指向的对象是基类还是派生类...当这样的一个指向派生类的基类指针析构时,如果析构函数不是虚函数,则直接调用基类的析构函数,那么派生类获取的资源未释放,则会造成内存泄漏。...而当析构函数是虚函数时则先调用对应的派生类析构函数,再调用基类析构函数,资源全部释放。...对于派生类的构造函数而言,进入其中时基类部分已构造完而派生类部分未构造完,对象类型是基类,故而此时调用虚函数,实际上使用的是基类的虚函数。 析构函数同理。...进入析构函数后派生类部分呈未定义值,对象类型是基类,调用的是基类的虚函数。 总而言之,在构造函数与析构函数中虚函数的行为有特殊变化;为了避免出错,不要在其过程中使用虚函数。
---- 二、基类和派生类对象赋值转换 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。...基类对象不能赋值给派生类对象。 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。...派生类的operator=必须要调用基类的operator=完成基类的复制。 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。...继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。...继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。
缺点的解决: 数据冗余:通过下面“虚继承”技术来解决(见下) 访问不明确(二义性):通过作用域访问符::来明确调用。...:为了保证公共继承对象在创建时只保存一分实例 虚继承解决了菱形继承的两个问题: 数据冗余:顶级基类在整个体系中只保存了一份实例 访问不明确(二义性):可以不通过作用域访问符::来调用(原理就是因为顶级基类在整个体系中只保存了一份实例...) 共享的基类对象成为“虚基类” 说明:虚继承不会影响派生类本身,只是对虚基类进行的说明 通过在继承列表中使用virtual关键字来说明,virtual与继承说明符(public、protected、private...)的位置可以互换 演示案例 下面的ZooAnimal是一个虚基类,Bear和Raccoon分别虚继承于ZooAnimal ?...{}; 三、虚继承中的类型转换 虚继承中也可以将派生类抓换为基类,用基类的指针/引用指向于派生类 菱形继承中的类型转换 菱形继承中会发生错误,不能将派生类转换为基类 原理是差不多的,就是因为派生类中拥有多份基类的实体
析构的时候,先是派生类先析构,然后是基类析构。 书中的补充①:需要注意的是编译器产生的析构函数并非虚函数。...书中补充②编译器拒绝为类生出operator=的情况: 第一种情况:类的成员变量中,存在引用的声明 第二种情况:存在const修饰的成员变量。...p = s;//error,报错显示operator是已删除的函数 return 0; } 赋值不成功的理由很简单,引用的指向是不可以被改变的,赋值的话就说明要改变引用指向的对象。...这种现象根本的原因在于:在派生类对象调用基类的构造函数期间,由于是基类先构造,那么在此期间,此时的对象被视为是基类的对象,并且派生类的成分并没有初始化,因此C++的做法是视它们不存在,这样才能保证安全。...,通过调用了createdogs函数,创建了基类Dog的匿名对象,也就是调用了基类的构造函数,然后进入了count_Dog函数,最后再次去调用派生类的构造函数的主体!
s; //赋值给指针 Person* ptrp = &s; 注:基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。...但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换。...并且注意到基类_num的用法是通过基类::基类成员 显示访问的。..._num) {} ⭐3.派生类的operator=必须要调用基类的operator=完成基类的复制 同样的,基类成员会去调用基类的赋值函数,如果是派生类自己的那就需要调用自己的赋值函数。...因为派生类与基类都有operator=(),因此在调用基类的operator=()的时候,需要指定类域,传入派生类的对象进行切片处理即可!
但只有通过显示类型转换,才可以将基类对象的地址赋给派生类指针(向下转换),而使用这样的指针不一定安全。 9. 可以将派生类对象赋给基类对象吗?可以将基类对象赋给派生类对象吗?...仅当派生类定义了转换运算符(即包含将基类引用作为唯一参数的构造函数)或使用基类为参数的赋值运算符时,相反的赋值才是可能的。 10. 假设定义了一个函数,它将基类对象的引用作为参数。...为什么该函数也可以将派生类对象作为参数? 应为c++允许基类引用指向从该基类派生而来的任何类型。 11. 假设定义了一个函数,它将基类对象作为参数(即函数按值传递基类对象)。...按值传递对象将调用复制构造函数,由于形参是基类对象,因此将调用基类的复制构造函数,复制构造函数已基类引用为参数,该引用可以将指向作为参数传递的派生对象,最终的结构是,将生成一个新的基类对象,其成员对应于派生类对象的基类部分...按值传递对象的主要有点在于可以保护原始数据,但可以通过将引用作为const类型传递,来达到同样的目的。 13. 假设Corporation是基类,PublicCorporation是派生类。
而必须通过基类的方法进行访问,具体的说就是派生类的构造函数必须使用基类的构造函数,其他的类比 RatedPlayer :: RatedPlayer(unsigned int r,const &fn,const...注意:如果在派生类中重新定义基类的方法,通常将基类方法声明为虚的,好处是程序将根据对象类型而不是引用或者指针的类型来选择方法版本,算是一个惯例吧 函数实现我就不写了,不然篇幅太长了,感谢!...虚函数 虚函数源于c++中的类继承,是多态的一种。在c++中,一个基类的指针或者引用可以指向或者引用派生类的对象。同时,派生类可以重写基类中的成员函数。...基类中 可以在基类中将被重写的成员函数设置为虚函数,其含义是:当通过基类的指针或者引用调用该成员函数时,将根据指针指向的对象类型确定调用的函数,而非指针的类型。...值得注意的是 只能通过指向基类的指针或基类对象的引用来调用虚函数,其格式为:指向基类的指针变量名->虚函数名(实参表)或基类对象的引用名.虚函数名(实参表) 实现动态联编需要同时满足以下三个条件:
是有构造函数特点决定的 虚函数使用条件必须是通过指针或者引用调用 构造函数无法通过指针直接调用。只能通过 placement new方式调用。...我的误区 根本说不清楚:直接回答vptr构造 不相关, 不懂 构造函数无法通过指针访问原理,new 如何调用类构造函数申请的用法。...根本说不清楚:必须创建好了,才能用,不懂 抽象和具体关系.接口不属于具体一个类 析构函数可以是虚函数吗,为什么 参考思路: 可以是 析构函数 执行顺序是 派生类 ,基类 如果析构函数不被声明成虚函数,则编译器实施静态绑定...,在删除指向派生类的基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。...如果声明了,触发 派生类 ,基类 正确析构顺序。
面使用,实际中扩展维护性不强 二、基类和派生类对象赋值转换 1、派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。...; Person* pp = &sobj; Person& rp = sobj; //反之 基类对象不能赋值给派生类对象 sobj = pobj; } 3、基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用...但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换。...派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。 3. 派生类的operator=必须要调用基类的operator=完成基类的复制。...②:当前对象与包含的对象是一个低耦合关系,如果修改包含对象的类中代码不需要修改当前对象类的代码。 ③:对象组合是通过获得对其他对象的引用而在运行时刻动态定义的。
父类 = 子类,会对子类进行切片,把父类的部分给基类进行赋值。 也可以使用引用和指针,同样也是通过切片来进行赋值。都可以对派生类进行修改。 引用 引用就是创建一个子类中基类部分的别名。...指针 指针就是将子类中基类的地址赋值给基类指针。 注意: 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。...寓意把派生类中父类那部分切来赋值过去。 基类对象不能赋值给派生类对象。 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。...派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。 派生类的operator=必须要调用基类的operator=完成基类的复制。..._d = 5; return 0; } 来看调试的过程: 通过这个逐语句调试的内存变化,我们可以确定大致的内存情况: 不使用虚拟继承就是这样的内存情况,也好理解为什么同名变量的两份是如何储存的了
面使用,实际中扩展维护性 二、基类和派生类对象赋值转换 1.派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。...这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。 2.基类对象不能赋值给派生类对象。 3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。...但是必须是基类的指针是指向派生类对象时才是安全的。...派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。 3. 派生类的operator=必须要调用基类的operator=完成基类的复制。 4....这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
因为可以切割,却不能补充,因此这样在引用/指针的时候是不对的。 当然,也不绝对,有几个例子是可以将基类对象赋值给派生类对象的,但这里知道即可,后续会补充上。 3....基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。...派生类的operator=必须要调用基类的operator=完成基类的复制。 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。...即B中地址指向的大小为20,C中地址指向的大小为12,通过左侧内存已的两段地址相减,正好一一对应,而这两个数字就是举例虚基类对象的偏移量。为什么会有偏移量?...继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。
那么可能在读取未初始化的值会导致不明确的行为。 为了避免不确定的问题,最佳的处理方法就是:永远在使用对象之前将它初始化。...最后还有一个情况:如果某个基类将 operator= 函数声明为 private ,编译器将拒绝为其派生类生成 operator= 函数。...多态性质基类需声明 virtual 析构函数 如果在多态性质的基类,没有声明一个 virtual 析构函数,那么在 delete 基类指针对象的时候,只会调用基类的析构函数,而不会调用派生类的析构函数,...问题出在 pa 指针指向派生类对象,而那个对象却经由一个基类指针被删除,而目前的基类没有 virtual 析构函数。...说明,基类构造期间 virtual 函数绝不会下降到派生类阶层。取而代之的是,对象的作为就像隶属于基类类型一样。
2.2继承关系和访问限定符 继承关系符指的是在派生类的类名加冒号后面的字符 访问限定符指的是对成员的声明 用一张图说明: 需要特别注意的是: (1)基类private成员还是继承到派生类中,但是无论什么方式都访问不了...(2)class默认继承方式是private,struct默认继承方式是public 二.基类和派生类对象赋值转换 派生类对象可以赋值给基类对象/基类引用/基类指针,这样的操作有个形象的说法叫切片或者切割...基类对象不能赋值给派生类对象。 基类指针或引用可以通过强制类型转换赋值给派生类指针或引用,但是只有在基类的指针是指向派生类的时候才是安全的。但是建议尽量不要这样操作。...将派生类给基类,发生了类型转换,但是并没有产生临时变量(特殊处理) 三.继承中的作用域 3.1概念 在继承体系中基类和派生类都有自己独立的作用域 当基类和派生类中存在同名成员,则派生类中的成员会对基类中的同名成员的直接访问进行屏蔽...派生类的拷贝构造函数必须调用基类的拷贝构造函数实现基类的构造。 派生类的赋值重载operator=必须调用基类的赋值重载实现基类的复制。
2.2 派生类对象赋值给基类对象的引用 那除此之外呢,还可以这样搞: 还可以赋给Person类对象的引用,即子类对象可以赋值给基类对象的引用。...那对它解引用就相当于拿到一个父类的对象,这个父类对象可以认为是从子类对象中切出来的属于父类的那一部分。 2.4 基类对象不能赋值给派生类对象 我们刚才是把派生类对象赋值给基类,那反过来可以吗?...基类可以赋值给派生类吗? 是不行的,基类对象不能赋值给派生类对象。...我们发现这里我们自己初始化继承下来的_name成员但是报错了。 为什么不行呢?那这里要告诉大家的是: 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。...4.3 赋值重载 那赋值重载呢其实也是一样,我们这里就不再那么仔细的分析了 派生类的operator=必须要调用基类的operator=完成基类部分的赋值。
2.基类和派生类对象赋值转换 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用 。这里有个形象的说法叫切片 或者切割。...我在Person类和Student类都写了一个fun函数,这两个fun函数只有参数不同,那么是构成重载吗?不是,这两个函数构成隐藏 ,为什么呢?...因为两个类的作用域是独立的,而构成重载的前提是同一作用域。 如果出现基类和派生类定义了同名成员,可以使用使用 基类::基类成员 显示访问。...派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。 3. 派生类的 operator= 必须要调用基类的 operator= 完成基类的复制。 4....因为先析构基类的话,万一派生类使用了基类的成员,而且基类已经被析构,有可能出现基类资源已经清理释放掉了,然后派生类还去访问了基类的成员,就会存在野指针的问题。
静态类型的类变量:在编译时就已经知道是什么类型的了 动态类型的类变量:自己所指的类型不明确,直到运行时才知道 如果表达式既不是引用也不是指针,那么其就没有静态类型和动态类型的概念,因为其只能与自己类型一致的对象绑定到一起...演示案例 当我们使用基类的引用(或指针)时,我们并不清楚该引用(或指针)所绑定的对象的真实类型,该对象可能是基类的对象,也可能是派生类的对象。...A,且指针也为A,因此调用A的getA()函数 A 20:虽然p2指针指向的类类型为B,但是访问规则只与指针/引用的类类型有关,而与指针/引用指向的类型无关。...main() { D x; B *pB = &x; pB->mf(); //调用D::mf() D *pD = &x; pD->mf(); //调用D::mf() return 0; } 三、为什么不建议派生类隐藏基类的...如果: 我们在派生类中隐藏了基类的non-virtual函数,那么基类与派生类就会产生行为上的不一致,is-a关系就消失了 如果想要表现出派生类与基类的不同,那么应该将函数声明为virtual(其中虚析构函数是一个例子
隐藏:不用作用域, 通过派生类访问:派生类同名函数,隐藏基类函数 或者通过基类指针访问,基类函数隐藏派生类。 二、 问题:如何解隐藏问题?...为了让隐藏起来的名字重见天日,使用using声明 通过base类指针或者引用访问 (这个和虚函数无关) 情况2 如果是通过通过base类指针或者引用访问 隐藏派生类同名函数。...三、如何将隐藏行为覆盖掉 情况1 如果是通过派生类访问一个函数,派生类局部作用域隐藏上层 base类函数 为了让隐藏起来的名字重见天日,使用using声明 通过base类指针或者引用访问 (这个和虚函数无关...) 情况2 如果是通过通过base类指针或者引用访问 隐藏派生类同名函数。...为了让隐藏起来的名字重见天日,使用using声明 通过base类指针或者引用访问 (这个和虚函数无关) 情况2 如果是通过通过base类指针或者引用访问 隐藏派生类同名函数。
所以就会报错 2.2派生类对象的引用赋值给基类对象 派生类对象的引用赋值能够给基类对象,其中引用不许需要const,证明其赋值之间并没有发生隐式类型转换,产生临时对象。...这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。 基类对象不能赋值给派生类对象。 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。...但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI,dynamic_cast 来进行识别后进行安全转换。...答案当然是隐藏关系,因为函数重载针对的是同一个作用域的函数,而基类与派生类直接作用域不同。 在隐藏关系中,同名函数默认调用的当前作用域的函数,如果想调用其他作用域的函数,则需要使用域作用限定符。...这种通过生成派生类的复用通常被称为**白箱复用(white - box reuse)。**术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。
领取专属 10元无门槛券
手把手带您无忧上云