第01节:继承
第02节:构造函数和析构函数
第03节:函数重定义
第04节:多态和虚函数
第05节:访问控制 (可见性控制)
第06节:抽象类与纯虚函数
第07节:动态类型转换
第01节:继承
回顾
面向对象的4个特点:
A(抽象) P(多态)I(继承)E(封装)
前两个单元:AE
本单元: PI
第02节:构造函数和析构函数
派生类继承的成员
派生类继承 ctor 和 dtor 吗?
派生类不继承的特殊函数
构造函数 (C++11已允许继承)
析构函数
作为特权地位的友元函数
赋值运算符函数
#include <iostream> struct A { A(int i) { } A(double d, int i) { } // ... }; struct B : A { using A::A; // 继承基类构造函数 int d{ 0 }; // 新的变量初始化方法 }; int main() { B b(1); // b.d 初始化为 0 }
Calling Base Class Constructors ( 调用基类构造函数)
Ctors of base class can only be invoked from the constructors of the derived classes. ( 基类构造函数只能由派生类构造函数调用)
The syntax to invoke it is as follows:
DerivedClass(parameterList) : BaseClass() { // Perform initialization } // Or DerivedClass(parameterList) : BaseClass(argumentList) { // Perform initialization }
No-Arg Constructor in Base Class( 基类的无参构造函数)
Rules for invoke constructors in derived class
A constructor in a derived class must always invoke a constructor in its base class. (派 生类构造函数必须调用基类构造函数)
If a base constructor is not invoked explicitly, the base class’s no-arg constructor is invoked by default. ( 若基类ctor未被显式调用,基类的无参构造函数就会被调用)
Constructor and Destructor Chaining ( 构造和析构函数链)
constructor chaining (构造函数链)
Constructing an instance of a class invokes all the base class along the inheritance chain. ( 构造类实例会沿着 继承链调用所有的基类ctor)
Invoke sequence: base first, derive next
destructor chaining (析构函数链)
Conversely, the destructors are automatically invoked in reverse order(dtor 与ctor正好相反)
Invoke sequence: derive first, base next
no-arg constructor ( 无参构造函数)
If a class is designed to be extended, provide a no-arg constructor. (若你的类 想被别人扩展,那么就提供一个无参构造函数)
34. 文件扩展名:头文件用.h,源文件用 .cpp (c++, cc也可)
35. A class should be declared in a header file and defined in a source file where the name of the files match the name of the class.
35. 类应该在头文件中声明并在源文件中定义,俩文件名字应 该与类名相同
例如:MyClass.h, MyClass.c++
例外的是,模板类的声明和定义都要放在头文件中
49. Class variables should never be declared public.
49. 类成员变量不可被声明为public
说明:公有变量违背了C++的信息隐藏原则。例外的是, 如果class只是一个数据结构,类似C语言中的struct,则 可将类变量声明为公有
#include <iostream> class Fruit { public: Fruit() { } Fruit(int id) { } std::string s; }; class Apple : public Fruit { public: Apple() :Fruit() { } }; int main() { Apple apple; }
第03节:函数重定义
Redefine (hide) ( 重定义/隐藏)
#include <iostream> #include <string> class GeometricObject { public: std::string toString() { return "parent"; } }; class Circle : public GeometricObject { public: std::string toString() { return "child"; } void g() { std::cout << toString(); } }; int main() { Circle circle; std::cout << circle.toString(); circle.GeometricObject::toString(); system("pause"); return 0; }
Redefine v.s. Overload ( 重定义与重载)
Overload Functions (§5.7) (重载函数)
more than one function with the same name (多个函数名字相同)
But different in at least one of the signatures: (但至少一个特征不同)
parameter type ( 参数类型)
parameter number ( 参数数量)
parameter sequence (参数顺序)
Redefine Functions (重定义函数)
The functions have the same signature (函数特征相同)
Name (同名)
Parameters (including type, number and sequence) (同参数:类型,数量和顺序)
Return type (返回值类型)
Defined in base class and derived class, respectively (在基类和派生类中分别定义)
第04节:多态和虚函数
What is Polymorphism?
广义的多态:不同对象对于相同的消息有不同的响应,就是OOP中的 多 态性。
截止目前:多态性有两种表现的方式
重载:
class C { public: int f(int x); int f(); };
重定义:不同的对象调用重定义 函数,表现出不同的行为
class A { int f() { return 1; } }; class B : public A { int f() { return 8; } }; A x; B y; x.f(); y.f();
Binding
联编(Binding): 确定具有多态性的 语句调用哪个函数的过程。
Static Binding (静态联编)
在程序编译时确定调用哪个函数
例:函数重载
Dynamic Binding (动态联编)
在程序运行时,才能够确定调用哪个 函数
用动态联编实现的多态,也称为运行 时的多态。
How do we implement polymorphism ( 如何实现多态)
virtual function (虚函数)
Override (覆盖) : redefining a virtual function in a derived class. ( 在派生类中重定义一个虚函数)
overload重载
override覆盖
overlord覆盖
Polymorphism: using dynamic binding ( 动态联编)
How to enable dynamic binding? ( 如何使得函数能够 实现动态联编)
The function must be declared virtual in the base class. ( 基类同 名虚函数)
The variable that references the object for the function must contain the address of the object. ( 访问对象的成员函数时,要用 指向对象的指针或者对象引用)
Note
If a function is defined virtual in a base class, it is automatically virtual in all its derived classes. ( 基类定义 了虚同名函数,那么派生类中的 同名函数自动变为虚函数)
Virtual functions:
Virtual function table (虚函数表)
Run-time binding (运行时联编)
More overhead in run-time than non- virtual function ( 开销大)
class C { public: virtual std::string toString() { return "class C"; } }; class B : public C { std::string toString() { return "class B"; } }; class A : public B { std::string toString() { return "class A"; } };
Summary: static binding v.s. dynamic binding
基类与派生类中有同名函数
1. 通过 派生类对象访问同名函数
静态联编
2. 通过 基类对象的指针访问同名函数
静态联编
3. 通过 基类对象的指针访问同名虚函数
动态联编
第05节:访问控制 (可见性控制)
The protected Keyword
the private and public keywords to specify whether data fields and functions can be accessed from the outside of the class. ( 说明数据及函数是否可以从类外面访问)
Private members can only be accessed from the inside of the class (私有成员只能在类 内的函数访问)
Public members can be accessed from any other classes. (公有成员可被任何其他类访 问)
A protected data field or a protected function in a base class can be accessed by name in its derived classes. ( 保护属性的数据或函数可被派生类成员访 问)
#include <iostream> using namespace std; class B { public: int i; protected: int j; private: int k; }; class A : public B { public: void display() { cout << i << endl; // Fine, cannot access it cout << j << endl; // Fine, cannot access it cout << k << endl; // Wrong, cannot access it } }; int main() { A a; cout << a.i << endl; // Fine, cannot access it cout << a.j << endl; // Wrong, cannot access it cout << a.k << endl; // Wrong, cannot access it return 0; }
1. 公有继承
公有继承的派生类定义形式:
class 派生类名:public 基类名
{
派生类新成员定义;
};
1. 基类成员 在派生类中的访问属性不变。
2. 派生类的成员函数 可以访问基类的公有成员和保护成员,不能 访问基类的私有成员;
3. 派生类以外的其它函数 可以通过派生类的对象,访问从基类继 承的公有成员, 但不能访问从基类继承的保护成员和私有成员。
2. 私有继承
私有继承的派生类定义形式:
class 派生类名:private 基类名
{
派生类新成员定义;
};
1. 基类成员 在派生类中的访问属性都变成 private。
2. 派生类的成员函数 可以访问基类的公有成员和保护成员,不能 访问基类的私有成员;
3. 派生类以外的其它函数 不能通过派生类的对象,访问从基类继 承的任何成员。
3. 保护继承
私有继承的派生类定义形式: class 派生类名:protected 基类名
{
派生类新成员定义;
};
1. 基类成员 公有成员和保护成员在派生类中变成保护类型的,基类 的私有成员属性不变。
2. 派生类的成员函数 可以访问基类的公有成员和保护成员,不能 访问基类的私有成员;
3. 派生类以外的其它函数 不能通过派生类的对象,访问从基类继 承的任何成员。
第06节:抽象类与纯虚函数
Abstract Classes ( 抽象类)
classes become more specific & concrete with each new derived class. ( 派生类时,新类会越来越明确和具 体)
move back up to the parent and ancestor , the classes become more general and less specific. ( 沿着派生类 向父类移动,类会越来越一般化和 抽象)
Sometimes a base class is so abstract that it cannot have any specific instances. Such a class is referred to as an abstract class ( 类太抽象以至于无法实例 化就叫做抽象类)
Abstract Functions ( 抽象函数)
All geometric objects have: areas & perimeters
Declare getArea() and getPerimeter() in GeometricObject class? (在几何对象类中声明 计算面积和周长的函数?)
Is it meaningful for getArea() in GeometricObject ? (这种声明有实际意义 吗?)
NO, the implementation is dependent on the specific type of geometric object.
virtual double getArea() = 0;
virtual double getPerimeter() = 0;
Such functions are referred to as abstract functions.
Abstract class: the class which contains abstract functions
第07节:动态类型转换
Dynamic Casting Example
dynamic_cast operator cast a parameter of the GeometricObject type into a Circle type ( 将基 类类型参数转换为派 生类类型)
then invoke the getRadius() and getDiameter() functions defined in the Circle class ( 然后调用派生类 中独有的函数)
// A function for displaying a geometric object void displayGeometricObject(GeometricObject &object) { cout << "The area is " << object.getArea() << endl; cout << "The perimeter is " << object.getPerimeter() << endl; GeometricObject *p = &object; Circle *p1 = dynamic_cast<Circle*>(p); if (p1 != 0) { cout << "The radius is " << p1->getRadius() << endl; cout << "The diameter is " << p1->getDiameter() << endl; } }
Upcasting and Downcasting ( 向上/向下 转型)
upcasting : Assigning a pointer of a derived class type to a pointer of its base class type ( 将派生类类型指针赋值给基类类型指针)
downcasting : Assigning a pointer of a base class type to a pointer of its derived class type. ( 将基类类型指针赋值给派生类类型指针)
Upcasting and Downcasting ( 向上/向下 转型 续)
Upcasting can be performed implicitly without using the dynamic_cast operator. ( 上转可不适用dynamic_cast而隐式转换)
GeometricObject *g = new Circle(1);
Circle *c = new Circle(2);
g = c; //Correct
However, downcasting must be performed explicitly. ( 下转必须显式执行)
For example, to assign p to p1, you have to use
c = dynamic_cast<Circle *>(g);
typeid operator (typeid 运算符)
How to obtain the information about the class of the object? (如何获取对象所 属的类的信息)
typeid operator: return a reference to an object of class type_info. (typeid运算 符返回一个type_info对象的引用)
to display the class name for object x. (显示对象x的类名)
#include <iostream> #include <string> int main() { std::string x; std::cout << typeid(x).name() << std::endl; system("pause"); return 0; }
基类对象和派生类对象的互操作
问题1:对象内存布局
GeometricObject G;
Circle C;
GeometricObject* pG=&G;
Circle* pC=&C;
问题2:互操作
G=C; //Y
C=G; //N
pG=&C; //Y
pC=&G; //N
GeometricObject &rG=C; //Y
Circle &rC=G; //N
Warning
1. 可将派生类对象截断,只使用继承来的信息
2. 但不能将基类对象加长,无中生有变出派生类对象