继承(二)

隐藏/重定义:子类和父类有同名的成员,子类成员隐藏了父类的成员。

重载:同一个作用域,重载了参数。

(在实际中最好不要定义同名函数)

子类对象不能初始化父类对象,用父类成员初始化子类成员。

析构函数会自动调用。

显示调用父类析构,无法保证先子后父,所以子类析构函数完成舅自定义调用父类析构,这样就保证了先子后父。

友元关系不能继承。

子类继承父类的静态成员,继承的是使用权(地址一样)

面经:

1. 什么是菱形继承?菱形继承的问题是什么?
答:菱形继承(Diamond Inheritance)是指在多继承的编程语言中出现的一种特定的继承模式。这种模式涉及到四个类:一个基类(A),两个派生类(B 和 C),以及一个从这两个派生类继承的顶层类(D)。
问题:数据冗余、二义性。
二义性:当两个派生类 B 和 C 都覆盖或使用了来自基类 A 的某个成员时,顶层类 D 将不知道应该使用哪一个版本,因为每个派生类都有自己的 A 类的副本。例如,如果 A 有一个方法 func(), B 和 C 都继承并可能修改了这个方法,那么 D 中调用 func() 时就会产生疑问:它应该调用哪个版本?
二义性解决办法: C++:使用虚继承(virtual inheritance)。虚继承确保了即使有多个派生路径,也只有一个基类实例存在。这样可以避免二义性问题。
2. 什么是菱形虚拟继承?如何解决数据冗余和二义性的
答:菱形虚拟继承是C++中解决菱形继承问题的一种机制。菱形继承是指一个类同时从两个类继承,而这两个类又共同从另一个类继承的情况。这种继承模式可能会导致数据冗余和二义性问题。

如何实现菱形虚拟继承

为了使用菱形虚拟继承,你需要在中间层类(即图中的B和C)中将对基类A的继承声明为虚拟继承。下面是一个例子:

class A {
public:
    int data;
};

class B : virtual public A { /* ... */ };

class C : virtual public A { /* ... */ };

class D : public B, public C {
public:
    void setData(int value) { data = value; } // 此处的 data 不会产生二义性
};

在这个例子中,BC 类都使用 virtual public A 来声明对 A 类的继承。这确保了尽管 D 类从 BC 继承,但是 A 类的数据成员 data 只会被继承一次。

  • 使用virtual关键字:在中间层类(如B和C)中使用virtual关键字修饰继承。
  • 仅在必要时使用:虚拟继承会增加类层次结构的复杂度,并且可能会影响性能(因为它需要额外的间接性来支持虚拟基类的唯一实例),因此只在需要时使用。
  • 虚拟继承的影响:虚拟继承不仅解决了数据冗余和二义性问题,还会影响构造函数和析构函数的调用顺序,因此需要特别注意这一点。
3. 继承和组合的区别?什么时候用继承?什么时候用组合?
答:
很多人说 C++ 语法复杂,其实多继承就是一个体现。有了多继承 ,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。
多继承可以认为是 C++ 的缺陷之一,很多后来的 OO 语言都没有多继承,如 Java
继承和组合
public 继承是一种 is-a 的关系。也就是说每个派生类对象都是一个基类对象。
组合是一种 has-a 的关系。假设 B 组合了 A ,每个 B 对象中都有一个 A 对象。
优先使用对象组合,而不是类继承
继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse) 。术语 白箱 是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。
对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse) ,因为对象的内部细节是不可见的。对象只以 黑箱 的形式出现。
组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合。
  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多重继承中的义性问题指的是当一个派生类同时继承自两个或多个具有相同成员函数或成员变量的基类时,可能会导致成员函数或成员变量的义性。 考虑以下示例代码: ```cpp class Base1 { public: void display() { std::cout << "Base1 display()" << std::endl; } }; class Base2 { public: void display() { std::cout << "Base2 display()" << std::endl; } }; class Derived : public Base1, public Base2 { public: void show() { // display(); // 义性错误,无法确定调用哪个基类的 display() Base1::display(); // 显式指定调用 Base1 的 display() Base2::display(); // 显式指定调用 Base2 的 display() } }; ``` 在上述代码中,Derived 类同时继承自 Base1 和 Base2 两个基类,并且这两个基类都有一个名为 `display()` 的成员函数。当在 Derived 类中调用 `display()` 函数时,由于存在两个同名函数,编译器无法确定调用哪个基类的 `display()` 函数,从而导致义性错误。 为了解决这个问题,我们可以使用作用域解析运算符 `::` 来显式地指定调用哪个基类的成员函数,如在上述代码中的 `show()` 函数中所示。 另一种解决义性问题的方法是使用虚继承(Virtual Inheritance),它可以确保在多重继承中只有一个共享的基类实例,从而消除了义性问题。通过在继承链中的虚基类前添加关键字 `virtual`,可以声明虚继承。使用虚继承后,派生类会直接访问虚基类的成员,而不会出现义性。 总结起来,多重继承中的义性问题是指当派生类同时继承自多个具有相同成员函数或成员变量的基类时,可能会导致成员访问的义性。可以通过作用域解析运算符和虚继承来解决这个问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值