关于C++思考(五)

前言

C + +最重要的性能之一是代码重用。但是,为了具有可进化性,我们应当能够做比拷贝代码更多的工作。
C++的解决方法:组合和继承
组合:简单地创建一个包含已存在的类对象的新类,这个新类是由已存在类的对象组合的。
继承:创建一个新类作为一个已存在类的类型,采取这个已存在类的形式,对它增加代码,但不修改它,其中大量的工作由编译器完成。

组合

实际上,我们一直都在用组合创建类,只不过我们是在用内部数据类型组合新类。其实使用用户定义类型组合新类同样很容易。

class X
{
    int i;
    enum { factor = 11 };
public:
    X():i(0)
    {
    }
    void set(int I)
    {
        i = I;
    }
    int read() const
    {
        return i;
    }
    int permute()
    {
        return i *= factor;
    }
};
class Y
{
    int i;
public:
    X x;
    Y():i(0)
    {
    }
    void f(int I)
    {
        i = I;
    }
    int g() const
    {
        return i;
    }
};

组合的语法很明显!

继承

Y继承于X,它意味着 Y 将包含 X 中的所有数据成员和成员函数。实际上Y包含了 X 的一个子对象,就像在 Y 中创建 X 的一个成员对象,而不从 X 继承一样。无论成员对象还是基类存储,都被认为是子对象。
在 C + +中保证合适的初始化表达式非常重要,当创建一个对象时,必须保证编译器调用所有子对象的构造函数。
解决办法很简单:对于子对象调用构造函数, C + +为此提供了专门的语法,即构造函数的初始化表达式表。
构造函数的初始化表达式表允许我们显式地调用成员对象的构造函数。
*主要思想是,在进入新类的构造函数体之前调用所有的构造函数。
这样,对子对象的成员函数所做的任何调用都已经转到了这个被初始化的对象中。没有对所有的成员对象和基类对象的构造函数调用,就没有办法进入这个构造函数的左括号,即便是编译器也必须对缺省构造函数做隐藏调用。这是 C + +进一步的强制,以保证没有调用它的构造函数就没有对象(或对象的部分)能进入第一道门。***
private 继承的目的是什么,因为在这个新类中选择创建一个 p r i v a t e对象似乎更合适。将 p r i v a t e继承包含在该语言中只是为了语言的完整性。但是,如果没有其他理由,则应当减少混淆,所以通常建议用 p r i v a t e成员而不是 p r i v a t e继承。
当私有继承时,基类的所有 p u b l i c成员都变成了 p r i v a t e。如果希望它们中的任何一个是可视的,只要用派生类的 p u b l i c选项声明它们的名字即可。

class base
{
    public:
        char f() const{}
        int g() const{}
        float h() const{}
}
class derived : base
{
    public:
        base:: f;
        base:: h;
}

在理想世界中, p r i v a t e成员总是严格私有的,但
在实际项目中,有时希望某些东西隐藏起来,但仍允许其派生类的成员访问。于是关键字p r o t e c t e d派上了用场。它的意思是: “就这个类的用户而言,它是 p r i v a t e的,但它可被从这个类继承来的任何类使用。 ”

组合与继承的联合

class A
{
    int i;
public:
A(int I):i(I){}
~A(){}
void f()const
{
}   
};
class B()
{
    int i;
public:
    B(int I):i(I){}
    ~B(){}
    void f() const{}
};
class C: public B
{
    A a;
public:
    C(int I):B(I),a(I){}
    ~C(){}
    void f() const
    {
        a.f();
        B::f();
    }
};

从C中可以看到构造函数的初始化表达式表中调用了基类构造函数和成员对象构造函数。
注意,只能在继承期间重定义函数。
构造函数和析构函数与众不同之处在于每一层函数都被调用,这是值得强调的。然而对于一般的成员函数,只是这个函数被调用,而不是任意基类版本被调用。如果还想调用一般成员函数的基类版本,必须显式地调用。
构造在类层次的最根处开始,而在每一层,首先调用基类构造函数,然后调用成员对象构造函数。调用析构函数则严格按照构造函数相反的次序—这是很重要的,因为要考虑潜在的相关性。另一有趣的是,对于成员对象,构造函数调用的次序完全不受在构造函数的初始化表达式表中次序的影响。该次序是由成员对象在类中声明的次序所决定的。如果能通过构造函数的初始化表达式表改变构造函数调用次序,那么就会对两个不同的构造函数有二种不同的调用顺序。而析构函数不可能知道如何为析构函数相应地反转调用次序,这就引起了相关性问题。

不能自动继承的函数

不是所有的函数都能自动地从基类继承到派生类中的。构造函数和析构函数是用来处理对象的创建和析构的,它们只知道对在它们的特殊层次的对象做什么。所以,在整个层次中的所有的构造函数和析构函数都必须被调用,也就是说,构造函数和析构函数不能被继承。另外, operator= 也不能被继承。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值