具有类继承和具有基类作为派生类中的数据成员之间有明显的区别吗?

Posted

技术标签:

【中文标题】具有类继承和具有基类作为派生类中的数据成员之间有明显的区别吗?【英文标题】:Is there a sensible difference between having class inheritence and having a base class as a data member in derived class? 【发布时间】:2018-06-08 09:18:21 【问题描述】:

我对面向对象的范式还是有点陌生​​,因此关于 OOP 的一件事让我感到困惑。这听起来可能是一个天真的问题,答案很明显,而且很可能是一个,但以下两种情况有什么区别:

案例一:

class A

public:
    A() = default; 
    A(int i) : data_point(i) ; 
    virtual int process_data_point();

protected: 
    int data_point=0;
;

class B : public A

public:
    int process_data_point() override;
;

案例2:

class A

int data_point=0;

public:
    A() = default; 
    A(int i) : data_point(i) ; 
    int process_data_point();
;


class B

public:
    A process_data_point_in_a_different_way();
    A data_point
;

我的意思是,我知道案例 1 处理的是继承,这符合 OOP 的精神,但我无法摆脱这样一种感觉,即通过使用基类并定义包含数据成员的新类也可以模拟继承基类,具有一些附加功能(如案例 2 中所示)。我知道从长远来看模拟继承可能会变得过于复杂(特别是如果最终从数据成员类中获得一些不需要的功能),但我希望对于一些简单的情况,继承模拟可以正常工作。

有人可以更详细地说明这一点吗?关于仿真的想法本质上是不好的,还是在某些情况下有意义?

【问题讨论】:

您说的是 is-a(继承)与 has-a 关系。 继承提供了组合不提供的多态性。 对于继承,请尝试说“... is a ...”来描述关系。对于封装,请尝试说“......有一个......”来描述这种关系。如果你不能(或类似情况),你应该检查你是如何建模关系的。 在这种情况下(A),virtual 触摸带来了另一个方面:它关于一个合约,通过从 A 继承,您同意实现一个process_data_point,它在一个@ 987654325@,保证调用者它必须在那里。 考虑用谷歌搜索“继承和封装之间的区别”,很多命中。 【参考方案1】:

区别1

在“继承”主题下还有另一个主题称为“多态性” - 以相同方式与不同对象相关的能力。使用继承方式时,可以制作接口类,以后在多态的情况下使用。快速浏览一下这段代码:

class BaseClass 
public:
    virtual void action() = 0;
;

class A : public BaseClass 
public:
    void action() 
        cout << "A class" << endl;
    
;

class B : public BaseClass 
public:
    void action() 
        cout << "B class" << endl;
    
;

在这种情况下,我们有 3 个类。 BaseClass 包含抽象函数“action”,以及两个继承类。下面的“main”函数展示了如何以多态的方式使用它们:

int main() 
    A a;
    B b;
    vector<BaseClass*> all = vector<BaseClass*>();
    all.push_back(&a);
    all.push_back(&b);

    for (size_t i = 0; i < all.size(); i++) 
        all[i]->action();
    

输出将是:

A class
B class

但是你看,我没有打电话给a.action()b.action()——我在一个循环中给他们打电话all[i]-&gt;action()。如果没有继承,您将无法做到这一点。

区别2

类中的“受保护”权限仅授予类本身和继承类的访问权限。让我们看看另一个例子:

class BaseClass 
private:
    void private_method() 
        cout << "Base class private method" << endl;
    
protected:
    void protected_method() 
        cout << "Base class protected method" << endl;
    
public:
    void public_method() 
        cout << "Base class public method" << endl;
    
;

假设我们有这个类,其中包括private_methodprotected_methodpublic_method。现在我们有两个不同的类。继承BaseClass的类A和包含BaseClass的类B。让我们看看我们可以在每个类中使用哪些方法:

class A : public BaseClass 
public:
    void test_privileges() 
        //this->private_method();   // Compilation error!
        this->protected_method();
        this->public_method();
    
;

class B 
private:
    BaseClass bc;

public:
    void test_privileges() 
        //bc.private_method();      // Compilation error!
        //bc.protected_method();    // Compilation error!
        bc.public_method();
    
;

如您所见,继承类和包含类之间存在保护差异。

区别3

当您设计代码时,您希望它对每个人都尽可能清晰。为此,您必须在代码中保持逻辑流程。继承一个类和包含它之间存在逻辑差异。 当你继承一个类时,你可以说继承者类是继承类,例如:

class Animal ;
class Dog : public Animal ;
class Cat : public Animal ;

Dog 是 Animal -> 所以 Dog 类必须继承 Animal 类。 当你在另一个类中包含一个类时,你可以说: contains 类有 include 类,例如:

class Tail ;
class Eye ;
class Dog 
private:
    Tail tail;
    Eye eye;
;

狗有尾巴。 -> 所以 Dog 类包含 Tail 类。 狗有眼睛。 -> 所以 Dog 类包含 Eye 类。

【讨论】:

以上是关于具有类继承和具有基类作为派生类中的数据成员之间有明显的区别吗?的主要内容,如果未能解决你的问题,请参考以下文章

c++继承是如何工作的?

继承和派生—— 类与类之间的关系继承的基本概念

5继承与派生2-访问控制

继承与派生

C++_练习—继承_公有继承

详解C++中基类与派生类的转换以及虚基类