C++ 面向对象高级编程 (上) week 3 (Boolan)

课程目标

  • 以良好的方式编写C++ class
    (Object Based: 面对的是单一class的设计)
    • class without pointer members -- Complex (week 1)
    • class with pointer members -- String (week 2)
  • 学习Classes之间的关系
    (Object Oriented: 面对的是多重classes的设计, classes和classes之间的关系)
    • 继承 (inheritance) (week 3)
    • 复合 (composition) (week 3)
    • 委托 (delegation) (week 3)

Composition (复合), 表示has-a

template >
class queue {
  // ...
 protected:
  Sequence c;  // 默认情况下: queue has a deque
 public:
  // 以下完全利用c的操作函数完成
  bool empty() const { return c.empty(); }
  size_type size() const  { return c.size(); }
  reference front() { return c.front(); }
  reference back() { return c.back();  }
  void push(const value_type& x) { c.push_back(x); }
  void pop() { c.pop_front(); }
};
  • deque (double ended queue) 是两端可进出
  • queue 是末端进前端出(先进先出FIFO)

分析: 由于deque可以从首位两端插入或剔除元素, 所以只需要对其进行简单的封装就可以分别实现先进先(FIFO)的stack和先进后出(FILO)的queue了. stack和queue中都有一个deque类型的成员, 用做数据存储的容器, 然后对deque的部分接口进行简单的封装, 例如stack只提供从末端插入和删除的接口以及获取末端元素的接口, 而queue则只提供从尾部插入而从头部删除的接口以及获取首位元素的接口. 像这样具有“修改某物接口, 形成另一种风貌”的性质的, 称为配接器(adapter), 因此STL中stack和queue往往不被归类为容器(container), 而被归类为容器配接器(container adapter).

  • 关于Composition (摘自Thinking in C++)

When place an object of that class inside a new class, we call this “creating a member object.” Your new class can be made up of any number and type of other objects, in any combination that you need to achieve the functionality desired in your new class. Because you are composing a new class from existing classes, this concept is called composition (or more generally, aggregation ). Composition is often referred to as a “has-a” relationship, as in “a car has an engine.”
Composition comes with a great deal of flexibility. The member
objects of your new class are usually private, making them inaccessible to the client programmers who are using the class. This allows you to change those members without disturbing existing client code. You can also change the member objects at runtime, to dynamically change the behavior of your program.

  • 关于Composition关系下的构造和析构
    (e.g., Container has a Component )
    • 构造由内而外: Container的构造函数首先调用Component的default构造函数,然后才执行自己.
    • 析构由外而内: Container的析构函数首先执行自己,然后才调用Component的析构函数.

Delegation(委托) Composition by reference

String.h:

class StringRep;
class String {
 public:
  String();
  String(const char *s);
  String(const String &s);
  String& operator=(const String &s);
  ~String();
  // ...
 private:
  StringRep *rep;  // pimpl
};

String.cpp

#include "String.h"
namespace {
class StringRep {
  friend class String;
  StringRep(const char *s);
  ~StringRep();
  int count;
  char *rep;
};
}

String::String() { ... }
// ...

分析: String有一个指向StringRep的指针, String负责对外的接口,
StringRep负责设计的实现, String通过调用StringRep来实现功能, 这样的关系叫做委托(Delegation), 或者是Composition by reference (可以看成是Composition复合的一种特殊形式).

  • 关于pimpl的定义:
    pImpl是一种 C++ 编程技巧, 它将类A的实现细节放到分离的以不透明指针访问的类B中, 以从其对象(类A的对象)表示中移除实现细节.此技巧用于构造拥有稳定 ABI 的 C++ 库接口, 及减少编译时依赖.

Inheritance(继承), 表示is-a

struct _List_node_base
{
  _List_node_base *_M_next;
  _List_node_base *_M_prev;
};

template 
struct _List_node : public _List_node_base
{
  _Tp _M_data;
};

关于Inheritance (摘自Thinking in C++)

It seems a pity, however, to go to all the trouble to create a class and then be forced to create a brand new one that might have similar functionality. It’s nicer if we can take the existing class, clone it, and then make additions and modifications to the clone. This is effectively what you get with inheritance, with the exception that if the original class (called the base or super or parent class) is changed, the modified “clone” (called the derived or inherited or sub or child class) also reflects those changes.
A type does more than describe the constraints on a set of objects; it also has a relationship with other types. Two types can have characteristics and behaviors in common, but one type may contain more characteristics than another and may also handle more messages (or handle them differently). Inheritance expresses this similarity between types using the concept of base types and derived types. A base type contains all of the characteristics and behaviors that are shared among the types derived from it. You create a base type to represent the core of your ideas about some objects in your system. From the base type, you derive other types to express the different ways that this core can be realized.

  • 关于Inheritance(继承)关系下的构造和析构
    (e.g., Derived is a class derived from class Base)
    • 构造由内而外: Derived的构造函数首先调用Base的default构造函数然后才执行自己.
    • 析构由外而内: Derived的析构函数首先执行自己, 然后才调用Base的析构函数.

Inheritance with virtual function (虚函数)

class Shape {
 public:
  virtual void draw() const = 0;  // pure virtual
  virtual void error(const std::string &msg);  // virtual
  int objectID() const;  // non-virtual
  // ...
};

class Rectangle : public Shape { ... };
class Ellipse : public Shape { ... };
  • non-virtual 函数: 你不希望derived class重新定义(override, 覆写)它;
  • virtual 函数: 你希望derived class重新定义(override, 覆写)它,且你对它已有默认定义;

Member function that defines type-specific behavior. Calls to a
virtual made through a reference or pointer are resolved at run time, based on the type of the object to which the reference or pointer is bound. (摘自C++ Primer)

  • pure virtual 函数: 你希望derived class一定要重新定义(override, 覆写)它, 你对它没有默认定义.

Virtual function declared in the class header using = 0 just before the semicolon. A pure virtual function need not be (but may be) defined. Classes with pure virtuals are abstract classes. If a derived class does not define its own version of an inherited pure virtual, then the derived class is abstract as well. (摘自C++ Primer)

Derived class (派生类) 中的virtual function (虚函数) (摘自C++ Primer):

When a derived class overrides a virtual function, it may, but is not required to, repeat the virtual keyword. Once a function is declared as virtual, it remains virtual in all the derived classes.
A derived-class function that overrides an inherited virtual function must have exactly the same parameter type(s) as the base-class function that it overrides.
With one exception, the return type of a virtual in the derived class also must match the return type of the function from the base class. The exception applies to virtuals that return a reference (or pointer) to types that are themselves related by inheritance. That is, if D is derived from B, then a base class virtual can return a B* and the version in the derived can return a D*. However, such return types require that the derived-to-base conversion from D to B is accessible.

虚函数(virtual function)的重要性
虚函数是应在派生类中重新定义的成员函数. 当使用指针或对基类的引用来引用派生的类对象时, 可以为该对象调用虚函数并执行该函数的派生类版本. 虚函数确保为该对象调用正确的函数, 这与用于进行函数调用的表达式无关. 虚函数是面向对象编程实现Polymorphism(多态)的基本手段.

Polymorphism (多态)

摘自C++ Primer:

The key idea behind OOP is polymorphism. Polymorphism is derived from a Greek word meaning “many forms.” We speak of types related by inheritance as polymorphic types, because we can use the “many forms” of these types while ignoring the differences among them. The fact that the static and dynamic types of references and pointers can differ is the cornerstone of how C++ supports polymorphism.
When we call a function defined in a base class through a reference or pointer to the base class, we do not know the type of the object on which that member is executed. The object can be a base-class object or an object of a derived class. If the function is virtual, then the decision as to which function to run is delayed
until run time. The version of the virtual function that is run is the one defined by the type of the object to which the reference is
bound or to which the pointer points.
On the other hand, calls to nonvirtual functions are bound at compile time. Similarly, calls to any function (virtual or not) on an object are also bound at compile time. The type of an object is fixed and unvarying—there is nothing we can do to make the dynamic type of an object differ from its static type. Therefore, calls made on an object are bound at compile time to the version
defined by the type of the object.

你可能感兴趣的:(C++ 面向对象高级编程 (上) week 3 (Boolan))