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 T, class Sequence  = deque<T>>
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 <typename _Tp>
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.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 之前很热的台湾电影——我的少女时代,让少女心一度成为热议,那,少女心到底是什么呢?少女应该有一番怎样的模样? ...
    四夕同学阅读 509评论 0 0
  • 我不曾出远门 我不知下过雨 我在南方湿润的空气里 遭遇了鲜花 我所有的高尚 我所有的卑躬屈膝 以及 你
    公子启阅读 114评论 0 0
  • 一次不经意的回眸,那些曾是绿意盎然的树枝已经变成枯枝残叶。空气中的弧度开始变冷,阳光也仿佛失去了温热。往日的繁华慢...
    宓美人阅读 336评论 2 2
  • 她现在要做的事情 只是临崖 赏雪 听雨 采药 读书 读书 一直读书 书中有大道 一卷便胜过情爱无数 她一心奉道 谁...
    鹿77阅读 710评论 0 0
  • 最近特别幸运,我遇到了一些优秀的人,有机会近距离观察他们的工作,方法让我看到了另一个世界的光。 我说的那些优秀的人...
    地瓜周丨读书控阅读 345评论 0 2