公有/私有/保护继承 继承与组合 overload/overwrite/override之间的区别

公有/私有/保护成员

在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。
在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。
在关键字protected后面声明,与private类似,其差别表现在继承与派生时对派生类的影响不同

公有/私有/保护继承

private成员任何形式继承,均不可访问。private继承,所有成员私有化。protected继承,public转为protected。

#include <iostream>
using namespace std;

class Base
{
public:
    int x_;
protected:
    int y_;
private:
    int z_;
};

class PublicInherit : public Base
{
public:
    void Test()
    {
        x_ = 10;
        y_ = 20;
        //z_ = 30; error
    }
private:
    int a_;
};

class PublicPublicInherit : public PublicInherit
{
public:
    void Test()
    {
        y_ = 20;
    }
};

class PrivateInherit : private Base
{
public:
    void Test()
    {
        x_ = 10;
        y_ = 20;
        //z_ = 30; error
    }
};

int main(void)
{
    PublicInherit pub;
    pub.x_ = 20;

    PrivateInherit pri;
    //pri.x_ = 10; error
    return 0;
}

只能在初始化列表的成员

const 常量
引用
类的成员没有默认构造函数
基类没有默认构造函数的时候,基类的构造函数要在派生类构造函数初始化列表中调用

创建对象时,要初始类成员的每一个成员,如果没有在初始化列表里面,编译器会自动使用它的默认的构造函数进行初始化,但是它没有默认构造函数,所以会编译报错。

Base(const Base &other) : objb_(other.objb_), b_(other.b_)
{

}

Derived(const Derived &other) : d_(other.d_), objd_(other.objd_), Base(other)
{

}

派生类的拷贝构造函数,基类没有默认拷贝构造函数的时候,也需要调用基类的拷贝构造函数

继承与组合

无论是继承与组合,两者都需要使用构造函数的初始化列表去构造这些对象

组合是has-a的关系。通常是在希望新类内部具有已存在的类的功能时使用,而不是希望已存在类作为它的接口。组合通过嵌入一个对象以实现新类的功能,而新类用户看到的是新定义的接口,而不是来自老类的接口。

继承是is-a的关系。如果希望新类与已存在的类有相同的接口,在这基础上可以增加自己的成员。这时候需要用继承。

#include <iostream>
using namespace std;

class Base
{
public:
    Base() : x_(0), y_(48)
    {

    }
    int GetBaseX() const
    {
        return x_;
    }

    int GetBaseY() const
    {
        return y_;
    }
    void Show()
    {
        cout << "Base::Show ..." << endl;
    }
    int x_;
private:
    int y_; //继承后无法被直接访问,可通过GetBaseY访问
};

class Derived : public Base
{
public:
    Derived() : x_(0)
    {

    }
    int GetDerivedX() const
    {
        return x_;
    }
    void Show(int n)//与下面的show 构成重载,基类的show被隐藏
    {
        cout << "Derived::Show " << n << endl;
    }

    void Show()
    {
        cout << "Derived::Show ..." << endl;
    }
    int x_; //重定义x_,基类的x_被隐藏
};

//组合关系
class Test
{
public:
    Base b_;
    int x_;
};

int main(void)
{
    Derived d;
    d.x_ = 10;
    d.Base::x_ = 20; //访问被隐藏的基类x_;
    cout << d.GetBaseX() << endl;
    cout << d.GetDerivedX() << endl;
    cout << d.GetBaseY() << endl;

    d.Show();
    d.Base::Show();//访问被隐藏的基类show

    cout << sizeof(Base) << endl;
    cout << sizeof(Derived) << endl;
    cout << sizeof(Test) << endl;

    Test t;
    cout << t.x_ << endl;
    cout << t.b_.GetBaseX() << endl;
    cout << t.b_.GetBaseY() << endl;

    return 0;
}

程序输出

20
10
48
Derived::Show ...
Base::Show ...
8
12
12
-858993460
0
48

构造函数是为了保证对象的每个数据成员都被正确初始化,无参的默认构造函数,x被初始化为任意值。

overload/overwrite/override之间的区别

重载(overload)成员函数

  1. 相同的范围(在同一个类中)
  2. 函数名字相同
  3. 参数不同
  4. virtual关键字可有可无

覆盖(override)派生类与基类

  1. 不同的范围(分别位于派生类与基类)
  2. 函数名字相同
  3. 参数相同
  4. 基类函数必须有virtual关键字

隐藏(overwrite)(派生类与基类)

  1. 不同的范围(分别位于派生类与基类)
  2. 函数名与参数都相同,基类无virtual关键字
  3. 函数名相同,参数不同,virtual可有可无

当隐藏发生时,实际上是继承了但不可见。隐藏的数据成员,仍然占内存空间。如果在派生类的成员函数中想要调用基类的被隐藏函数,可以使用基类名::函数名(参数)的语法形式。如果被隐藏的函数是public的,则在类体外也可以使用派生类对象.基类名::函数名(参数) 的语法,也可用派生类指针->基类名::函数名(参数)的语法,同理被隐藏的数据成员也可以使用上述列举的方法访问。