《C专家编程》第十一章--C++

356 阅读6分钟

P242

OOP: 面向对象的编程的特点是继承和动态绑定。C++通过类的派生支持继承,通过虚拟函数支持动态绑定。虚拟函数提供了一种封装类体系实现细节的方法。

P243

抽象: 它是一个去除对象中不重要的细节过程,只有那些描述了对象的本质特征的关键点才被保留。抽象是一种设计活动,其他的概念都是提供抽象的OOP特性。

在软件中,抽象是非常有用的,因为它允许程序实现以下目标:

  • 隐藏不相关的细节,把注意力集中在本质特征上。
  • 向外界提供一个“黑盒子”接口。接口确定了施加在对象之上的有效操作的集合,但它并不提示对象在内部是怎样实现它们的。
  • 把一个复杂的系统分解成几个相互独立的组成部分。避免组件之间不符合规则的相互作用。
  • 重用和共享代码。

抽象建立了一种抽象数据类型,C++使用类(Class)这个特性来实现它。

P245

封装:把相关的类型、数据和函数组合在一起。

当你把抽象数据类型和他们的操作捆绑在一起的时候,就是在进行“封装”。非00P语言没有完备的机制来实现封装。我们没有办法告诉C编辑器“这三个函数只对这个特定的结构才有效”,也没有办法防止程序定义一个新的函数,以未经检查的和不一致的方式访问这个结构。

P246

类就是用户定义类型加上所有对该类进行的操作。
类经常被实现的形式是:一个包含多个数据的结构,加上对这些数据进行操作的函数的指针。编译器施行强类型--确保这些函数只会被该类的对象调用,而且该类的对象无法调用出他们之外的其他函数。

P247

访问控制是一个关键字,它说明了谁可以访问接下来声明的数据或函数。访问控制可以是下面3种之一:

  • public: 属于public的声明在类的外部是可见,并按需要进行设置。
  • protected: 只能由该类本身的函数以及从该类所派生的类的函数使用。
  • private: 只能被该类的成员函数使用。private声明在类外部是可见的,但却是不能访问的。
      除此之外。还有friendvirtual。这两个关键字每次只能用于一条声明,而上述关键字每个后面可以跟一大串声明。另一点不同的是,friendvirtual这两个关键字后面不跟冒号。
  • friend: 属于friend的函数不属于类的成员函数,但可以像成员函数一样访问peotectedprivatefriend可以是一个函数,也可以是一个类。
  • virtual: P258讲关于多态的知识。

P248

当成员函数在类的外部实现时,前面必须附加一个前缀::
::被称为“全局范围分解符”。跟在它前面的标识符就是进行查找的范围。如果::前面没有标识符,就表示查找范围为全局范围。

P250

this: 每个成员函数都有一个this指针参数,它是隐式赋给该函数的,它允许对象在成员函数内部引用对象本身。

构造函数和析构函数

  • 绝大多数类至少具有一个构造函数。当类的一个对象被创建时,构造函数被隐式地调用,它负责对象的初始化
  • 可以在类中声明多个构造函数,通过参数来区分它们。
  • 构造函数的名字总是和类的名字一样。
    例:
  //构造函数初始化
   Fruit::Fruit(int i, int j){...};
   //
   Fruit melon(4, 5);
  • 类也存在一个清理函数,称为析构函数。当对象被撤销(超出生存范围或进行delete操作,回收它所使用的堆内存)时,析构函数自动被调用。

继承--复用已经定义的操作

从一个类派生另外一个类,使前者的所有特征在后者中自动可用。它可以声明一些类型,这些类型可以共享部分或全部以前所声明的类型。它也可以从超过一个的基类型共享一些特征。

用书中的例子说明,C++如何进行继承。

class Fruit
{
  public:
      peel();
      slice();
      juice();
  private:
      int weight, calories_per_oz;
};
//基类为Fruit,派生类为Apple。
class Apple : public Fruit 
{
  public:
      void make_candy_apple(float weight);
      void bob_for(int tub_id, int number_of_attempts);
};

Apple teachers;

多重继承: 从两个或更多的基类派生。

多重继承看上去很困难,无论在实现上和使用上都是一个容易产生错误的特性。有些人认为迄今为止尚无令人信服的例子证明那种设计是必须采用多重继承的。

P254

重载: 作用于不同类型的同一操作具有相同的名字。

  • 重载(overload)就是简单地复用现存的名字,但使它操作一个不同的类型。
  • 重载总是在编译时进行解析。
  class Fruit
{
  public:
      peel();
      slice();
      juice();
      //operator是一个关键字,后面的“+”表示对“+”进行重载。
      int operator+(Fruit &f); //重载“+”操作符
  private:
      int weight, calories_per_oz;
};

int Fruit::operator+(Fruit &f){
  //每个成员函数都有一个隐式的this指针,它允许我们引用操作符的左操作数,  
  下式"+"号前面的weight等于this.weight
  return weight + f.weight; 
}

Apple apple;
Fruit orange;
int ounces = apple + orange;

C++的输入/输出(I/O)

c++使用<<操作符和>>操作符来代替C语言中的putchargetchar等函数。

使用操作符而不是函数来操作I/O具有几大优点:

  • 操作符可被定义,用于任何类型。这样就不需要为每种类型准备一个单独的函数或者字符串格式化限定符如%d
  • 与函数相比,当你输出多条信息时,使用操作符操纵I/O具有概念上的方便性。
    cout<<"the value is" << i << endl
  • 它提供一个附加的层,简化了类似scanf()这样的函数的格式控制和使用方法。

P258

多态: 指一个函数或者操作符只有一个名字,但它可以用于几个不同的派生类型的能力。

#include <stdio.h>
    class Fruit
  {
    public:
        void peel(){"peeling a base class fruit\n"};
        slice();
        juice();
    private:
        int weight, calories_per_oz;
  };
  
    class Apple : public Fruit 
  {
    public:
        void peel(){printf("peeling an apple\n");};
        void make_candy_apple(float weight);
  };
  
  Fruit banana;
  banana.peel();
  Fruit *p;
  p = new Apple;
  p->peel();

运行上述程序,将得到以下结果

peeling a bass class fruit;
peeling a bass class fruit;

出现上述结果的原因:没有显式的告诉编译器那些成员函数需要多态,通知的方法就是在可能会被取代的基类成员函数前面加上virtual关键字。