VC++入门经典学习笔记--继承和面向对象

1.面向对象的基本思想:

类是为适应特定应用程序的需求而定义的数据类型。面向对象编程中的类同时定义了与程序相关的对象。设计该问题的解决方案时,要根据某个问题所特有的对象,使用可以直接处理这些对象的操作。我们可以定义一个类来表示某种抽象的事物,如数学概念中的复数,或者物理概念中的开车。因此,除了是数据类型之外,类还可以定义现实世界中特定种类的一组对象,至少可以说是解决特定问题所需要的定义。

我们可以认为类定义了一组特定事物的特性,这种事物用一组公共的参数来表示,并使用用一组公共的,可以对它们进行处理的操作。可以应用于特定类对象的操作由类接口定义。它们对应于类定义的public部分包含的函数。

现实世界中有许多不同种类的箱子,如纸板箱,电脑机箱,糖果盒。说出来的只是很少的几个,我们肯定还能想到好多。从这几个箱子中,我们可以发现某些共同的特性–都是四方形的。因此,我们可以将某种箱子定义成具有所有箱子的一般特性:只是长,宽和高,然后给基本的箱子类型添加一些其他特性,从而将特定种类的箱子同其他箱子区别开来。我们还可能发现一些可以对特定种类的箱子执行,但不能对其他箱子执行的操作。

因此,相对准确地模拟现实世界的好方法,就是定义相互关联的类。可以将糖果盒视为具备所有基本箱子的特性,再加上少许自身特性的箱子。这句话准确地阐明了以某个类为基础定义另一个类时类之间的关系。更特殊化的类具有父类的所有特性,再加上少许区别性的自身特性。

2.类的继承

当以一个类为基础定义另一个类时,后者称为派生类。派生类自动包含用来定义自己的那个类的所有数据成员,还有条件地包含了函数成员。我们说该类继承了基类的数据成员和函数成员。
注意:派生类不继承的基类成员仅有析构函数,构造函数以及任何重载赋值运算符的成员函数。所有的其他成员都将由派生类继承。不继承某些基类成员的原因是派生类总是有自己的构造函数和析构函数。如果基类有赋值运算符,派生类也将提供自己的版本。我们说不继承这些函数,意思是它们不会作为派生类对象的成员存在。但它们任然作为某个对象的基础组成部分而存在。

3.基类的概念

任何用作定义其他类的基础的类都是基类。例如:如果直接根据类A定义类B,则A是B的直接基类。
仅仅因为继承了成员函数,并不意味着不需要在派生类中将它们替换成新版本,必要时当然可以这样做。

4.基类的派生类

1.创建win32控制台空白项目,添加CBox.h头文件

#pragma once             //防止多次嵌入Box.h。
class CBox
{
public:
    double m_Length;
    double m_Width;
    double m_Height;
    explicit CBox( double lv = 1.0, double wv = 1.0, double hv = 1.0 ):m_Length{ lv }, m_Width{ wv }, m_Height{ hv }{}
};

2.添加CCandyBox.h头文件

#pragma once
#include <cstring>
#include "CBox.h"

class CCandyBox :private CBox                            //默认为私有继承,如果要访问基类数据成员,
    //则需要改变访问说明符。如果省略基类的访问说明符,则编译器将默认该说明符是private。
{
public:
    char* m_Contents;
    explicit CCandyBox(const char* str = "Candy")
    {
        size_t length{ strlen(str) + 1 };
        m_Contents = new char[length];
        strcpy_s(m_Contents, length, str);
    }

    CCandyBox(const CCandyBox& box) = delete;
    CCandyBox& operator = (const CCandyBox& box) = delete;

    ~CCandyBox()
    {
        delete[] m_Contents;
    }
};

3.添加项目名称.cpp源文件

#include <iostream>
#include "CCandyBox.h"

int main()
{
    CBox myBox{ 4.0, 3.0, 2.0 };                       //创建一个基类箱子对象
    CCandyBox myCandyBox;                              
    CCandyBox myMintBox{"Water Thin Mints"};           //创建一个糖果盒对象

    std::cout << "myBox occupies" << sizeof myBox
        << " bytes" << std::endl
        << "myCandyBox occupies " << sizeof myCandyBox
        << " bytes" << std::endl
        << "myMintBox occupies " << sizeof myMintBox
        << " bytes" << std::endl;

    std::cout << "myBox length is " << myBox.m_Length << std::endl;
    myBox.m_Length = 10.0;
    system("pause");
    return 0;
}

运行结果:

myBox occupies24 bytes
myCandyBox occupies 32 bytes
myMintBox occupies 32 bytes
myBox length is 4

5.派生类中构造函数的操作:

虽派生类不会继承基类的构造函数,但它们仍然存在于基类中,并且用于创建派生类对象的基类部分。因为创建派生类对象的基类部分实际上属于基于基类构造函数而非派生类构造函数的任务。毕竟,虽然在派生类中继承了基类的私有成员,但是不可访问,因此这些任务必须交给基类的构造函数来完成。

创建派生类对象的基类部分,自动调用了默认的基类构造函数。但情况不一定这样。可以在派生类的构造函数 中安排调用特定的基类构造函数,这样就能用非默认的构造函数初始化基类的数据成员,实际上就是可以根据给派生类构造函数提供的数据,选择调用特定的类构造函数。

6.继承类成员的访问级别

如果在派生类的定义中没有为基类提供访问说明符,则默认的访问说明符是private,结果是从基类继承的public和protected成员在派生类中称为private。基类的private成员仍然是基类所私有的,因此不能被派生类的成员函数访问。事实上,无论派生类定义种为基类指定怎样的访问说明符,这些成员始终都是基类的私有成员。

  CCBox:CBox{};

我们还可以使用public作为基类的说明符。该说明符赋予派生类中的基类成员与原来相同的访问级别,因此public成员仍然是public,protected成员仍然是protected。

 CCBox:public CBox{};

最后一种可能性是将基类声明为protected,结果是从基类继承的piblic成员在派生类中成为protected成员,而protected继承的成员(和private继承的成员)在派生类中仍然保持原来的访问级别。

  CCBox:protected CBox{};

将其简化为与派生类的继承成员有关的3点:
如果将基类的成员声明为private,则它们在派生类中永远都不可访问。
如果将基类声明为public,其成员在派生类中的访问级别保持不变。
如果将基类声明为protected,其public成员在派生类中将成为protected。

能够改变派生类中继承成员的访问级别,给予我们一定程度的灵活性,但不要忘记基类中指定的级别是不能放宽的。只能使访问级别更加严格,这意味着如果想在派生类中改变访问级别,则基类需要有public成员。这一点似乎与为了保护数据不受非授权访问而将其封装在类中的思想相侼,但如后面所述,经常需要以这样的方式定义基类,因为它们的唯一用途是作为定义其他类的基础,而非用来实例化对象。

7.派生类中的复制构造函数

注意:在声明用相同的类对象初始化的对象时,会自动调用复制构造函数。

  CBox myBox{2.0,3.0,4.0};
  CBox copyBox{myBox};

第一条语句调用接受3个double类型实参的构造函数,第二条语句调用复制构造函数。如果不提供自己的复制构造函数,则编译器将提供一个默认的复制构造函数,将初始化对象的成员逐一复制到新对象的对应成员中。

  CBox(const CBox& initB)
  {
    std::cout<<"CBox copy constructor called"<<std::endl;
    m_Length = initB.m_Length;
    m_Width = initB.m_Width;
    m_Height = initB.m_Height;
  }

注意:为了避免无穷无尽地调用自身,必须将复制构造函数的形参指定为引用,否则将需要复制以传值方式传递的实参。当调用该示例中的复制构造函数时,它向屏幕上输出一条消息,因此从输出中可以看出事件发生的时间。现在需要做的只是向派生类中添加复制构造函数。

  CCandyBox(const CCandyBox& initB):CBox(initB)
  {
    std::cout<<"CCandyBox copy constructor called"<<std::endl;
    size_t length{strlen(initB.m_Contents)+1};
    m_Contents = new char[length];
    strcpy_s(m_Contents,length,initB.m_Contents);
  }

注意:在为派生类编写构造函数时,需要初始化包括继承成员在内的派生类对象的所有成员。

8.禁止派生类final

有时需要确保不能把类用作基类,为此可以把类指定为final

  class CBox final
  {
    //代码
  };

定义中类名后面的final修饰符告诉编译器不允许从CBox类中派生。
注意:final不是关键字,只在这个上下文中有特殊的含义。不能把关键字用作名称,但可以把final用作变量名。

9.友元类成员

友元函数有权自由访问任何类成员。当然,我们没有理由说友元函数不能是另一个类的成员。
1.新建CBottle.h

#pragma once
class CCarton;                //前置声明
class CBottle
{
public:
    CBottle(double height, double d) : m_Height{ height }, m_d{ d }{}
public:
    double m_Height;
    double m_d;

    friend CCarton::CCarton(const CBottle& aBottle);
};

2.新建CCarton.h

#pragma once
class CBottle;             //前置声明 
class CCarton
{
public:
    CCarton(const CBottle& aBottle);
private:
    double m_Height;
    double m_Width;
    double m_Length;
};

3.新建项目名称.cpp

#include "CCarton.h"
#include "CBottle.h"

CCarton::CCarton(const CBottle& aBottle)
{
    m_Height = aBottle.m_Height;
    m_Length = 4.0*aBottle.m_d;
    m_Width = 3.0*aBottle.m_d;
}

10.友元类

还可以使某个类的所有函数成员都有权访问另一个类的数据成员,只需要将该类声明为友元类即可。通过在CBottle类定义内添加一条友元声明,可以将CCarton类定义为CBottle类的友元:

  friend CCarton;

CCBox类中有了这条声明语句之后,CCarton类所有函数成员就都能自由访问CBottle类的所有数据成员。

11.类友元关系的限制

类友元关系不是互惠的。使CCarton类成为CBottle类的友元,并不意味着CBottle类也是CCarton类的友元。如果希望如此,则必须在CCarton类中将CBottle类声明为友元。
类友元关系还是不可继承的。如果以CBottle为基类定义了另一个类,则CCarton类的成员将无权访问该派生类的数据成员,即使这些成员是从CBottle类继承的也不行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值